From 31410355d46751763031f802d89f92bb97b7b5c4 Mon Sep 17 00:00:00 2001 From: Ghanshyam Mann Date: Fri, 27 Nov 2020 21:56:32 -0600 Subject: [PATCH] Retire the python-searchlightclient project As announced in openstack-discuss ML[1], Searchlight project is retiring in Wallaby cycle. This commit retires python-searchlightclient repository as per process deinfed in project-guide[2]. Anyone would like to maintain it again, please revert back this commit and propose the re-adding it to governance. The community wishes to express our thanks and appreciation to all of those who have contributed to the python-searchlightclient project over the years. Depends-On: https://review.opendev.org/c/openstack/project-config/+/764519 Needed-By: https://review.opendev.org/c/openstack/governance/+/764530 [1] http://lists.openstack.org/pipermail/openstack-discuss/2020-November/018637.html [2] https://docs.openstack.org/project-team-guide/repository.html#retiring-a-repository Change-Id: If02fcc1c21abc9040a3b81cfa4973c7c6382ea15 --- .gitignore | 20 - .stestr.conf | 3 - .zuul.yaml | 8 - CONTRIBUTING.rst | 16 - LICENSE | 175 ------ README.rst | 158 +----- doc/Makefile | 90 --- doc/requirements.txt | 7 - doc/source/conf.py | 220 -------- doc/source/contribute/index.rst | 20 - doc/source/index.rst | 24 - doc/source/man/openstack.rst | 7 - doc/source/man/search.rst | 78 --- lower-constraints.txt | 71 --- .../notes/drop-py-2-7-5ef11499fe79e1ca.yaml | 6 - requirements.txt | 15 - searchlightclient/__init__.py | 0 searchlightclient/client.py | 60 -- searchlightclient/common/__init__.py | 0 searchlightclient/common/base.py | 530 ------------------ searchlightclient/common/exceptions.py | 477 ---------------- searchlightclient/common/utils.py | 313 ----------- searchlightclient/exc.py | 191 ------- searchlightclient/i18n.py | 25 - searchlightclient/osc/__init__.py | 0 searchlightclient/osc/plugin.py | 61 -- searchlightclient/osc/v1/__init__.py | 0 searchlightclient/osc/v1/facet.py | 79 --- searchlightclient/osc/v1/resource_type.py | 49 -- searchlightclient/osc/v1/search.py | 136 ----- searchlightclient/tests/__init__.py | 0 searchlightclient/tests/unit/__init__.py | 0 searchlightclient/tests/unit/osc/__init__.py | 0 searchlightclient/tests/unit/osc/fakes.py | 67 --- searchlightclient/tests/unit/osc/utils.py | 93 --- .../tests/unit/osc/v1/__init__.py | 0 searchlightclient/tests/unit/osc/v1/fakes.py | 89 --- .../tests/unit/osc/v1/test_facet.py | 70 --- .../tests/unit/osc/v1/test_resource_type.py | 51 -- .../tests/unit/osc/v1/test_search.py | 133 ----- searchlightclient/tests/unit/v1/__init__.py | 0 .../tests/unit/v1/test_facets.py | 44 -- .../tests/unit/v1/test_resource_types.py | 25 - .../tests/unit/v1/test_search.py | 73 --- searchlightclient/v1/__init__.py | 0 searchlightclient/v1/client.py | 42 -- searchlightclient/v1/facets.py | 53 -- searchlightclient/v1/resource_types.py | 31 - searchlightclient/v1/search.py | 58 -- setup.cfg | 35 -- setup.py | 20 - test-requirements.txt | 10 - tools/install_venv.py | 75 --- tools/install_venv_common.py | 170 ------ tools/with_venv.sh | 10 - tox.ini | 77 --- 56 files changed, 8 insertions(+), 4057 deletions(-) delete mode 100644 .gitignore delete mode 100644 .stestr.conf delete mode 100644 .zuul.yaml delete mode 100644 CONTRIBUTING.rst delete mode 100644 LICENSE delete mode 100644 doc/Makefile delete mode 100644 doc/requirements.txt delete mode 100644 doc/source/conf.py delete mode 100644 doc/source/contribute/index.rst delete mode 100644 doc/source/index.rst delete mode 100644 doc/source/man/openstack.rst delete mode 100644 doc/source/man/search.rst delete mode 100644 lower-constraints.txt delete mode 100644 releasenotes/notes/drop-py-2-7-5ef11499fe79e1ca.yaml delete mode 100644 requirements.txt delete mode 100644 searchlightclient/__init__.py delete mode 100644 searchlightclient/client.py delete mode 100644 searchlightclient/common/__init__.py delete mode 100644 searchlightclient/common/base.py delete mode 100644 searchlightclient/common/exceptions.py delete mode 100644 searchlightclient/common/utils.py delete mode 100644 searchlightclient/exc.py delete mode 100644 searchlightclient/i18n.py delete mode 100644 searchlightclient/osc/__init__.py delete mode 100644 searchlightclient/osc/plugin.py delete mode 100644 searchlightclient/osc/v1/__init__.py delete mode 100644 searchlightclient/osc/v1/facet.py delete mode 100644 searchlightclient/osc/v1/resource_type.py delete mode 100644 searchlightclient/osc/v1/search.py delete mode 100644 searchlightclient/tests/__init__.py delete mode 100644 searchlightclient/tests/unit/__init__.py delete mode 100644 searchlightclient/tests/unit/osc/__init__.py delete mode 100644 searchlightclient/tests/unit/osc/fakes.py delete mode 100644 searchlightclient/tests/unit/osc/utils.py delete mode 100644 searchlightclient/tests/unit/osc/v1/__init__.py delete mode 100644 searchlightclient/tests/unit/osc/v1/fakes.py delete mode 100644 searchlightclient/tests/unit/osc/v1/test_facet.py delete mode 100644 searchlightclient/tests/unit/osc/v1/test_resource_type.py delete mode 100644 searchlightclient/tests/unit/osc/v1/test_search.py delete mode 100644 searchlightclient/tests/unit/v1/__init__.py delete mode 100644 searchlightclient/tests/unit/v1/test_facets.py delete mode 100644 searchlightclient/tests/unit/v1/test_resource_types.py delete mode 100644 searchlightclient/tests/unit/v1/test_search.py delete mode 100644 searchlightclient/v1/__init__.py delete mode 100644 searchlightclient/v1/client.py delete mode 100644 searchlightclient/v1/facets.py delete mode 100644 searchlightclient/v1/resource_types.py delete mode 100644 searchlightclient/v1/search.py delete mode 100644 setup.cfg delete mode 100644 setup.py delete mode 100644 test-requirements.txt delete mode 100644 tools/install_venv.py delete mode 100644 tools/install_venv_common.py delete mode 100755 tools/with_venv.sh delete mode 100644 tox.ini diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 195533e..0000000 --- a/.gitignore +++ /dev/null @@ -1,20 +0,0 @@ -.coverage* -.venv -*,cover -cover -*.pyc -AUTHORS -build -dist -ChangeLog -run_tests.err.log -.tox -doc/source/api -doc/build -*.egg -*.eggs -searchlightclient/versioninfo -*.egg-info -*.log -.stestr/ -*.swp diff --git a/.stestr.conf b/.stestr.conf deleted file mode 100644 index f27e450..0000000 --- a/.stestr.conf +++ /dev/null @@ -1,3 +0,0 @@ -[DEFAULT] -test_path=./searchlightclient/tests -top_dir=./ diff --git a/.zuul.yaml b/.zuul.yaml deleted file mode 100644 index 5a78c1e..0000000 --- a/.zuul.yaml +++ /dev/null @@ -1,8 +0,0 @@ -- project: - templates: - - openstack-python3-wallaby-jobs - - openstack-cover-jobs - - publish-openstack-docs-pti - - check-requirements - - openstackclient-plugin-jobs - - openstack-lower-constraints-jobs diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst deleted file mode 100644 index 4d0a62a..0000000 --- a/CONTRIBUTING.rst +++ /dev/null @@ -1,16 +0,0 @@ -If you would like to contribute to the development of OpenStack, -you must follow the steps documented at: - - https://docs.openstack.org/infra/manual/developers.html#development-workflow - -Once those steps have been completed, changes to OpenStack -should be submitted for review via the Gerrit tool, following -the workflow documented at: - - https://docs.openstack.org/infra/manual/developers.html#development-workflow - -Pull requests submitted through GitHub will be ignored. - -Bugs should be filed on Storyboard, not GitHub: - - https://storyboard.openstack.org/#!/project_group/searchlight diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 67db858..0000000 --- a/LICENSE +++ /dev/null @@ -1,175 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. diff --git a/README.rst b/README.rst index a094784..86e34d6 100644 --- a/README.rst +++ b/README.rst @@ -1,152 +1,10 @@ -======================== -Team and repository tags -======================== +This project is no longer maintained. -.. image:: https://governance.openstack.org/tc/badges/python-searchlightclient.svg - :target: https://governance.openstack.org/tc/reference/tags/index.html +The contents of this repository are still available in the Git +source code management system. To see the contents of this +repository before it reached its end of life, please check out the +previous commit with "git checkout HEAD^1". -.. Change things from this point on - -======================== -python-searchlightclient -======================== - -OpenStack Indexing and Search API Client Library - -This is a client library for Searchlight built on the Searchlight API. It -provides a Python API (the ``searchlightclient`` module) and a command-line -tool (``searchlight``). - -The project is hosted on `Storyboard`_, where bugs can be filed. The code is -hosted on `OpenStack git repository`_. Patches must be submitted using -`Gerrit`_, *not* git repo -pull requests. - -.. _OpenStack git repository: https://opendev.org/openstack/python-searchlightclient -.. _Storyboard: https://storyboard.openstack.org/#!/project_group/searchlight -.. _Gerrit: https://docs.openstack.org/infra/manual/developers.html#development-workflow - -python-searchlightclient is licensed under the Apache License like the rest of -OpenStack. - -.. contents:: Contents: - :local: - -Install the client from PyPI ----------------------------- -The program `python-searchlightclient`_ package is published on `PyPI`_ and -so can be installed using the pip tool, which will manage installing all -python dependencies:: - - $ pip install python-searchlightclient - -.. note:: - The packages on PyPI may lag behind the git repo in functionality. - -.. _PyPI: https://pypi.python.org/pypi/python-searchlightclient/ - -Setup the client from source ----------------------------- - -* Clone repository for python-searchlightclient:: - - $ git clone https://opendev.org/openstack/python-searchlightclient.git - $ cd python-searchlightclient - -* Setup a virtualenv - -.. note:: - This is an optional step, but will allow Searchlightclient's dependencies - to be installed in a contained environment that can be easily deleted - if you choose to start over or uninstall Searchlightclient. - -:: - - $ tox -evenv --notest - -Activate the virtual environment whenever you want to work in it. -All further commands in this section should be run with the venv active: - -:: - - $ source .tox/venv/bin/activate - -.. note:: - When ALL steps are complete, deactivate the virtualenv: $ deactivate - -* Install Searchlightclient and its dependencies:: - - (venv) $ python setup.py develop - -Command-line API ----------------- - -Set Keystone environment variables to execute CLI commands against searchlight. - -* To execute CLI commands:: - - $ export OS_USERNAME= - $ export OS_PASSWORD= - $ export OS_TENANT_NAME= - $ export OS_AUTH_URL='http://localhost:5000/v2.0/' - -.. note:: - With devstack you just need to $ source openrc . And you can - work with a local installation by passing --os-token and --os-url - http://localhost:9393. You can also set up a `Openstackclient`_ config file - to work with the CLI. - -.. _Openstackclient: https://docs.openstack.org/developer/python-openstackclient/configuration.html#clouds-yaml - -:: - - $ openstack - (openstack) search resource type list - +--------------------------+--------------------------+ - | Name | Type | - +--------------------------+--------------------------+ - | OS::Designate::RecordSet | OS::Designate::RecordSet | - | OS::Designate::Zone | OS::Designate::Zone | - | OS::Glance::Image | OS::Glance::Image | - | OS::Glance::Metadef | OS::Glance::Metadef | - | OS::Nova::Server | OS::Nova::Server | - +--------------------------+--------------------------+ - -Here are the full list of subcommands, Use -h to see options: - - ============================= ======================================= - Subcommand Description - ============================= ======================================= - search facet list List Searchlight Facet - search resource type list List Searchlight Resource Type (Plugin) - search query Search Searchlight resource - ============================= ======================================= - -Python API ----------- - -To use with keystone as the authentication system:: - - >>> from keystoneclient.auth.identity import generic - >>> from keystoneclient import session - >>> from searchlightclient import client - >>> auth = generic.Password(auth_url=OS_AUTH_URL, username=OS_USERNAME, password=OS_PASSWORD, tenant_name=OS_TENANT_NAME) - >>> keystone_session = session.Session(auth=auth) - >>> sc = client.Client('1', session=keystone_session) - >>> sc.resource_types.list() - [...] - - -* License: Apache License, Version 2.0 -* Documentation: https://docs.openstack.org/developer/python-searchlightclient -* Source: https://opendev.org/openstack/python-searchlightclient -* Bugs: https://storyboard.openstack.org/#!/project_group/searchlight - -Testing -------- - -There are multiple test targets that can be run to validate the code. - -* tox -e pep8 - style guidelines enforcement -* tox -e py36 - traditional unit testing with python 3.6 -* tox -e py37 - traditional unit testing with python 3.7 +For any further questions, please email +openstack-discuss@lists.openstack.org or join #openstack-dev on +Freenode. diff --git a/doc/Makefile b/doc/Makefile deleted file mode 100644 index d5aca51..0000000 --- a/doc/Makefile +++ /dev/null @@ -1,90 +0,0 @@ -# Makefile for Sphinx documentation -# - -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -SPHINXSOURCE = source -PAPER = -BUILDDIR = build - -# Internal variables. -PAPEROPT_a4 = -D latex_paper_size=a4 -PAPEROPT_letter = -D latex_paper_size=letter -ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) $(SPHINXSOURCE) - -.PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest - -help: - @echo "Please use \`make ' where is one of" - @echo " html to make standalone HTML files" - @echo " dirhtml to make HTML files named index.html in directories" - @echo " pickle to make pickle files" - @echo " json to make JSON files" - @echo " htmlhelp to make HTML files and a HTML help project" - @echo " qthelp to make HTML files and a qthelp project" - @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" - @echo " changes to make an overview of all changed/added/deprecated items" - @echo " linkcheck to check all external links for integrity" - @echo " doctest to run all doctests embedded in the documentation (if enabled)" - -clean: - -rm -rf $(BUILDDIR)/* - -html: - $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." - -dirhtml: - $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." - -pickle: - $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle - @echo - @echo "Build finished; now you can process the pickle files." - -json: - $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json - @echo - @echo "Build finished; now you can process the JSON files." - -htmlhelp: - $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp - @echo - @echo "Build finished; now you can run HTML Help Workshop with the" \ - ".hhp project file in $(BUILDDIR)/htmlhelp." - -qthelp: - $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp - @echo - @echo "Build finished; now you can run "qcollectiongenerator" with the" \ - ".qhcp project file in $(BUILDDIR)/qthelp, like this:" - @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/python-searchlightclient.qhcp" - @echo "To view the help file:" - @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/python-searchlightclient.qhc" - -latex: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo - @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." - @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ - "run these through (pdf)latex." - -changes: - $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes - @echo - @echo "The overview file is in $(BUILDDIR)/changes." - -linkcheck: - $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck - @echo - @echo "Link check complete; look for any errors in the above output " \ - "or in $(BUILDDIR)/linkcheck/output.txt." - -doctest: - $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest - @echo "Testing of doctests in the sources finished, look at the " \ - "results in $(BUILDDIR)/doctest/output.txt." diff --git a/doc/requirements.txt b/doc/requirements.txt deleted file mode 100644 index 962946b..0000000 --- a/doc/requirements.txt +++ /dev/null @@ -1,7 +0,0 @@ -# The order of packages is significant, because pip processes them in the order -# of appearance. Changing the order has an impact on the overall integration -# process, which may cause wedges in the gate later. - -openstackdocstheme>=2.2.0 # Apache-2.0 -sphinxcontrib-svg2pdfconverter>=0.1.0 # BSD -sphinx>=2.0.0,!=2.1.0 # BSD diff --git a/doc/source/conf.py b/doc/source/conf.py deleted file mode 100644 index 6e2009e..0000000 --- a/doc/source/conf.py +++ /dev/null @@ -1,220 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -# This file is execfile()d with the current directory set to its containing -# dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -#sys.path.append(os.path.abspath('.')) - -# -- General configuration ---------------------------------------------------- - -# Add any Sphinx extension module names here, as strings. They can be -# extensions -# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = ['sphinx.ext.autodoc', - 'openstackdocstheme', - 'sphinxcontrib.rsvgconverter'] - -autoclass_content = 'both' - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# The suffix of source filenames. -source_suffix = '.rst' - -# The encoding of source files. -#source_encoding = 'utf-8' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -copyright = 'OpenStack Contributors' - -# openstackdocstheme options -openstackdocs_repo_name = 'openstack/python-searchlightclient' -openstackdocs_pdf_link = True -openstackdocs_bug_project = 'python-searchlightclient' -openstackdocs_bug_tag = '' - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -#language = None - -# There are two options for replacing |today|: either, you set today to some -# non-false value, then it is used: -#today = '' -# Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' - -# List of documents that shouldn't be included in the build. -#unused_docs = [] - -# List of directories, relative to source directory, that shouldn't be searched -# for source files. -exclude_trees = [] - -# The reST default role (used for this markup: `text`) to use for all -# documents. -#default_role = None - -# If true, '()' will be appended to :func: etc. cross-reference text. -add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -add_module_names = True - -# If true, sectionauthor and moduleauthor directives will be shown in the -# output. They are ignored by default. -#show_authors = False - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - -# A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] - -# Grouping the document tree for man pages. -# List of tuples 'sourcefile', 'target', u'title', u'Authors name', 'manual' - -man_pages = [ - ('man/openstack', 'openstack', 'OpenStack Search command line client', - ['OpenStack Contributors'], 1), -] - -# -- Options for HTML output -------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. Major themes that come with -# Sphinx are currently 'default' and 'sphinxdoc'. -html_theme = 'openstackdocs' - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -#html_theme_options = {} - -# Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] - -# The name for this set of Sphinx documents. If None, it defaults to -# " v documentation". -#html_title = None - -# A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None - -# The name of an image file (relative to this directory) to place at the top -# of the sidebar. -#html_logo = None - -# The name of an image file (within the static path) to use as favicon of the -# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -#html_favicon = None - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -# html_static_path = ['_static'] - -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -#html_use_smartypants = True - -# Custom sidebar templates, maps document names to template names. -#html_sidebars = {} - -# Additional templates that should be rendered to pages, maps page names to -# template names. -#html_additional_pages = {} - -# If false, no module index is generated. -#html_use_modindex = True - -# If false, no index is generated. -#html_use_index = True - -# If true, the index is split into individual pages for each letter. -#html_split_index = False - -# If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True - -# If true, an OpenSearch description file will be output, and all pages will -# contain a tag referring to it. The value of this option must be the -# base URL from which the finished HTML is served. -#html_use_opensearch = '' - -# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = '' - -# Output file base name for HTML help builder. -htmlhelp_basename = 'python-searchlightclientdoc' - - -# -- Options for LaTeX output ------------------------------------------------- - -# The paper size ('letter' or 'a4'). -#latex_paper_size = 'letter' - -# The font size ('10pt', '11pt' or '12pt'). -#latex_font_size = '10pt' - -latex_use_xindy = False - -latex_domain_indices = False - -latex_elements = { - # Additional stuff for the LaTeX preamble. - # openany: Skip blank pages in generated PDFs - 'extraclassoptions': 'openany,oneside', - 'makeindex': '', - 'printindex': '', - 'preamble': r'\setcounter{tocdepth}{3}', -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, author, documentclass [howto/manual]) -# . -latex_documents = [ - ('index', 'doc-python-searchlightclient.tex', - 'python-searchlightclient Documentation', - 'OpenStack Foundation', 'manual', True), -] - -# The name of an image file (relative to this directory) to place at the top of -# the title page. -#latex_logo = None - -# For "manual" documents, if this is true, then toplevel headings are parts, -# not chapters. -#latex_use_parts = False - -# Additional stuff for the LaTeX preamble. -#latex_preamble = '' - -# Documents to append as an appendix to all manuals. -#latex_appendices = [] - -# If false, no module index is generated. -#latex_use_modindex = True diff --git a/doc/source/contribute/index.rst b/doc/source/contribute/index.rst deleted file mode 100644 index e7b5dca..0000000 --- a/doc/source/contribute/index.rst +++ /dev/null @@ -1,20 +0,0 @@ -============ -Contributing -============ - -Code is hosted at `opendev.org`_. Submit bugs to the searchlight project -on `Storyboard`_. Submit code to the openstack/python-searchlightclient project -using `Gerrit`_. - -.. _opendev.org: https://opendev.org/openstack/python-searchlightclient -.. _Storyboard: https://storyboard.openstack.org/#!/project_group/93 -.. _Gerrit: https://docs.openstack.org/infra/manual/developers.html#development-workflow - -Testing -------- - -The preferred way to run the unit tests is using ``tox``. - -See `Consistent Testing Interface`_ for more details. - -.. _Consistent Testing Interface: https://opendev.org/openstack/governance/tree/reference/project-testing-interface.rst diff --git a/doc/source/index.rst b/doc/source/index.rst deleted file mode 100644 index 1f60c31..0000000 --- a/doc/source/index.rst +++ /dev/null @@ -1,24 +0,0 @@ -Python bindings to the OpenStack Searchlight API -================================================ - -This is a client for OpenStack Searchlight API. There's a Python API -(the :mod:`searchlightclient` module), and a command-line script -(installed as :program:`openstack`). - -Contents --------- - -.. toctree:: - :maxdepth: 2 - - contribute/index - - -Man Page --------- - -.. toctree:: - :maxdepth: 1 - - man/openstack - man/search diff --git a/doc/source/man/openstack.rst b/doc/source/man/openstack.rst deleted file mode 100644 index 30723f0..0000000 --- a/doc/source/man/openstack.rst +++ /dev/null @@ -1,7 +0,0 @@ -========= -openstack -========= - -Please refer to OpenStack command-line interface `OpenStackClient`_. - -.. _OpenStackClient: https://docs.openstack.org/python-openstackclient/latest/ diff --git a/doc/source/man/search.rst b/doc/source/man/search.rst deleted file mode 100644 index cbe1adf..0000000 --- a/doc/source/man/search.rst +++ /dev/null @@ -1,78 +0,0 @@ -====== -Search -====== - -search facet list ------------------ - -List Searchlight Facets. - -.. program:: search facet list -.. code:: bash - - openstack search facet list - [--type ] - [--limit-terms ] - [--all-projects] - -.. option:: --type - - Get facets for a particular resource type. - -.. option:: --limit-terms - - Restricts the number of options returned for fields that - support facet terms. - -.. option:: --all-projects - - Request facet terms for all projects (admin only). - -search resource type list -------------------------- - -List Searchlight Resource Type (Plugin). - -.. program:: search resource type list -.. code:: bash - - openstack search resource type list - -search query ------------- - -Search Searchlight resource. - -.. program:: search query -.. code:: bash - - openstack search query - --type [ [ ...]] - --all-projects - --source [[,...]] - --json - - -.. option:: --type [ [ ...]] - - One or more types to search. Uniquely identifies - resource types. Example: ``--type OS::Glance::Image - OS::Nova::Server`` - -.. option:: --all-projects - - By default searches are restricted to the current project - unless all_projects is set. - -.. option:: --source [[,...]] - - Whether to display the json source. If not specified, - it will not be displayed. If specified with no argument, - the full source will be displayed. Otherwise, specify - the fields combined with ',' to return the fields you want. - It is recommended that you use the ``--max-width`` argument - with this option. - -.. option:: --json - - Treat the query argument as a JSON formatted DSL query. diff --git a/lower-constraints.txt b/lower-constraints.txt deleted file mode 100644 index a20cb34..0000000 --- a/lower-constraints.txt +++ /dev/null @@ -1,71 +0,0 @@ -alabaster==0.7.10 -appdirs==1.3.0 -asn1crypto==0.23.0 -Babel==2.3.4 -cffi==1.14.0 -cliff==2.8.0 -cmd2==0.8.0 -coverage==4.0 -cryptography==2.7 -debtcollector==1.2.0 -decorator==3.4.0 -deprecation==1.0 -docutils==0.11 -dogpile.cache==0.6.2 -dulwich==0.15.0 -extras==1.0.0 -fixtures==3.0.0 -idna==2.6 -imagesize==0.7.1 -iso8601==0.1.11 -Jinja2==2.10 -jmespath==0.9.0 -jsonpatch==1.16 -jsonpointer==1.13 -jsonschema==2.6.0 -keystoneauth1==3.4.0 -linecache2==1.0.0 -MarkupSafe==1.0 -monotonic==0.6 -msgpack-python==0.4.0 -munch==2.1.0 -netaddr==0.7.18 -netifaces==0.10.4 -openstacksdk==0.11.2 -os-client-config==1.28.0 -os-service-types==1.2.0 -osc-lib==1.8.0 -oslo.config==5.2.0 -oslo.i18n==3.15.3 -oslo.serialization==2.18.0 -oslo.utils==3.33.0 -pbr==2.0.0 -positional==1.2.1 -prettytable==0.7.1 -pycparser==2.18 -Pygments==2.2.0 -pyOpenSSL==17.1.0 -pyparsing==2.1.0 -pyperclip==1.5.27 -python-cinderclient==3.3.0 -python-glanceclient==2.8.0 -python-keystoneclient==3.8.0 -python-mimeparse==1.6.0 -python-novaclient==9.1.0 -python-openstackclient==3.12.0 -python-subunit==1.0.0 -pytz==2013.6 -PyYAML==3.13 -requests==2.14.2 -requestsexceptions==1.2.0 -rfc3986==0.3.1 -simplejson==3.5.1 -snowballstemmer==1.2.1 -stevedore==1.20.0 -stestr==2.0.0 -testrepository==0.0.18 -testtools==2.2.0 -traceback2==1.4.0 -unittest2==1.1.0 -warlock==1.2.0 -wrapt==1.7.0 diff --git a/releasenotes/notes/drop-py-2-7-5ef11499fe79e1ca.yaml b/releasenotes/notes/drop-py-2-7-5ef11499fe79e1ca.yaml deleted file mode 100644 index b2ded1a..0000000 --- a/releasenotes/notes/drop-py-2-7-5ef11499fe79e1ca.yaml +++ /dev/null @@ -1,6 +0,0 @@ ---- -upgrade: - - | - Python 2.7 support has been dropped. Last release of python-searchlightclient - to support python 2.7 is OpenStack Train. The minimum version of Python now - supported is Python 3.6. diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 4831bcf..0000000 --- a/requirements.txt +++ /dev/null @@ -1,15 +0,0 @@ -# The order of packages is significant, because pip processes them in the order -# of appearance. Changing the order has an impact on the overall integration -# process, which may cause wedges in the gate later. - -Babel!=2.4.0,>=2.3.4 # BSD -pbr!=2.1.0,>=2.0.0 # Apache-2.0 -osc-lib>=1.8.0 # Apache-2.0 -PrettyTable<0.8,>=0.7.1 # BSD -oslo.i18n>=3.15.3 # Apache-2.0 -oslo.serialization!=2.19.1,>=2.18.0 # Apache-2.0 -oslo.utils>=3.33.0 # Apache-2.0 -python-keystoneclient>=3.8.0 # Apache-2.0 -python-openstackclient>=3.12.0 # Apache-2.0 -PyYAML>=3.13 # MIT -requests>=2.14.2 # Apache-2.0 diff --git a/searchlightclient/__init__.py b/searchlightclient/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/searchlightclient/client.py b/searchlightclient/client.py deleted file mode 100644 index f7676e6..0000000 --- a/searchlightclient/client.py +++ /dev/null @@ -1,60 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from keystoneclient import adapter - -from searchlightclient.common import utils -from searchlightclient import exc - - -def Client(version, *args, **kwargs): - module = utils.import_versioned_module(version, 'client') - client_class = getattr(module, 'Client') - return client_class(*args, **kwargs) - - -def _construct_http_client(**kwargs): - kwargs = kwargs.copy() - if kwargs.get('session') is None: - raise ValueError("A session instance is required") - - return SessionClient( - session=kwargs.get('session'), - auth=kwargs.get('auth'), - region_name=kwargs.get('region_name'), - service_type=kwargs.get('service_type', 'search'), - interface=kwargs.get('endpoint_type', 'public').rstrip('URL'), - user_agent=kwargs.get('user_agent', 'python-searchlightclient'), - endpoint_override=kwargs.get('endpoint_override'), - timeout=kwargs.get('timeout') - ) - - -class SessionClient(adapter.LegacyJsonAdapter): - def __init__(self, *args, **kwargs): - self.timeout = kwargs.pop('timeout', None) - super(SessionClient, self).__init__(*args, **kwargs) - - def request(self, *args, **kwargs): - kwargs.setdefault('raise_exc', True) - - if self.timeout is not None: - kwargs.setdefault('timeout', self.timeout) - - kwargs.setdefault('headers', {}).setdefault( - 'Content-Type', 'application/json') - - resp, body = super(SessionClient, self).request(*args, **kwargs) - - if kwargs.get('raise_exc') and resp.status_code >= 400: - raise exc.from_response(resp, body) - return resp diff --git a/searchlightclient/common/__init__.py b/searchlightclient/common/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/searchlightclient/common/base.py b/searchlightclient/common/base.py deleted file mode 100644 index 8b6f4c4..0000000 --- a/searchlightclient/common/base.py +++ /dev/null @@ -1,530 +0,0 @@ -# Copyright 2010 Jacob Kaplan-Moss -# Copyright 2011 OpenStack Foundation -# Copyright 2012 Grid Dynamics -# Copyright 2013 OpenStack Foundation -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -Base utilities to build API operation managers and objects on top of. -""" - -######################################################################## -# -# THIS MODULE IS DEPRECATED -# -# Please refer to -# https://etherpad.openstack.org/p/kilo-oslo-library-proposals for -# the discussion leading to this deprecation. -# -# We recommend checking out the python-openstacksdk project -# (https://launchpad.net/python-openstacksdk) instead. -# -######################################################################## - - -# E1102: %s is not callable -# pylint: disable=E1102 - -import abc -import copy - -from oslo_utils import strutils -from urllib import parse - -from searchlightclient.common import exceptions -from searchlightclient.i18n import _ - - -def getid(obj): - """Return id if argument is a Resource. - - Abstracts the common pattern of allowing both an object or an object's ID - (UUID) as a parameter when dealing with relationships. - """ - try: - if obj.uuid: - return obj.uuid - except AttributeError: - pass - try: - return obj.id - except AttributeError: - return obj - - -# TODO(aababilov): call run_hooks() in HookableMixin's child classes -class HookableMixin(object): - """Mixin so classes can register and run hooks.""" - _hooks_map = {} - - @classmethod - def add_hook(cls, hook_type, hook_func): - """Add a new hook of specified type. - - :param cls: class that registers hooks - :param hook_type: hook type, e.g., '__pre_parse_args__' - :param hook_func: hook function - """ - if hook_type not in cls._hooks_map: - cls._hooks_map[hook_type] = [] - - cls._hooks_map[hook_type].append(hook_func) - - @classmethod - def run_hooks(cls, hook_type, *args, **kwargs): - """Run all hooks of specified type. - - :param cls: class that registers hooks - :param hook_type: hook type, e.g., '__pre_parse_args__' - :param args: args to be passed to every hook function - :param kwargs: kwargs to be passed to every hook function - """ - hook_funcs = cls._hooks_map.get(hook_type) or [] - for hook_func in hook_funcs: - hook_func(*args, **kwargs) - - -class BaseManager(HookableMixin): - """Basic manager type providing common operations. - - Managers interact with a particular type of API (servers, flavors, images, - etc.) and provide CRUD operations for them. - """ - resource_class = None - - def __init__(self, client): - """Initializes BaseManager with `client`. - - :param client: instance of BaseClient descendant for HTTP requests - """ - super(BaseManager, self).__init__() - self.client = client - - def _list(self, url, response_key=None, obj_class=None, json=None): - """List the collection. - - :param url: a partial URL, e.g., '/servers' - :param response_key: the key to be looked up in response dictionary, - e.g., 'servers'. If response_key is None - all response body - will be used. - :param obj_class: class for constructing the returned objects - (self.resource_class will be used by default) - :param json: data that will be encoded as JSON and passed in POST - request (GET will be sent by default) - """ - if json: - body = self.client.post(url, json=json).json() - else: - body = self.client.get(url).json() - - if obj_class is None: - obj_class = self.resource_class - - data = body[response_key] if response_key is not None else body - # NOTE(ja): keystone returns values as list as {'values': [ ... ]} - # unlike other services which just return the list... - try: - data = data['values'] - except (KeyError, TypeError): - pass - - return [obj_class(self, res, loaded=True) for res in data if res] - - def _get(self, url, response_key=None): - """Get an object from collection. - - :param url: a partial URL, e.g., '/servers' - :param response_key: the key to be looked up in response dictionary, - e.g., 'server'. If response_key is None - all response body - will be used. - """ - body = self.client.get(url).json() - data = body[response_key] if response_key is not None else body - return self.resource_class(self, data, loaded=True) - - def _head(self, url): - """Retrieve request headers for an object. - - :param url: a partial URL, e.g., '/servers' - """ - resp = self.client.head(url) - return resp.status_code == 204 - - def _post(self, url, json, response_key=None, return_raw=False): - """Create an object. - - :param url: a partial URL, e.g., '/servers' - :param json: data that will be encoded as JSON and passed in POST - request (GET will be sent by default) - :param response_key: the key to be looked up in response dictionary, - e.g., 'server'. If response_key is None - all response body - will be used. - :param return_raw: flag to force returning raw JSON instead of - Python object of self.resource_class - """ - body = self.client.post(url, json=json).json() - data = body[response_key] if response_key is not None else body - if return_raw: - return data - return self.resource_class(self, data) - - def _put(self, url, json=None, response_key=None): - """Update an object with PUT method. - - :param url: a partial URL, e.g., '/servers' - :param json: data that will be encoded as JSON and passed in POST - request (GET will be sent by default) - :param response_key: the key to be looked up in response dictionary, - e.g., 'servers'. If response_key is None - all response body - will be used. - """ - resp = self.client.put(url, json=json) - # PUT requests may not return a body - if resp.content: - body = resp.json() - if response_key is not None: - return self.resource_class(self, body[response_key]) - else: - return self.resource_class(self, body) - - def _patch(self, url, json=None, response_key=None): - """Update an object with PATCH method. - - :param url: a partial URL, e.g., '/servers' - :param json: data that will be encoded as JSON and passed in POST - request (GET will be sent by default) - :param response_key: the key to be looked up in response dictionary, - e.g., 'servers'. If response_key is None - all response body - will be used. - """ - body = self.client.patch(url, json=json).json() - if response_key is not None: - return self.resource_class(self, body[response_key]) - else: - return self.resource_class(self, body) - - def _delete(self, url): - """Delete an object. - - :param url: a partial URL, e.g., '/servers/my-server' - """ - return self.client.delete(url) - - -class ManagerWithFind(BaseManager, metaclass=abc.ABCMeta): - """Manager with additional `find()`/`findall()` methods.""" - - @abc.abstractmethod - def list(self): - pass - - def find(self, **kwargs): - """Find a single item with attributes matching ``**kwargs``. - - This isn't very efficient: it loads the entire list then filters on - the Python side. - """ - matches = self.findall(**kwargs) - num_matches = len(matches) - if num_matches == 0: - msg = _("No %(name)s matching %(args)s.") % { - 'name': self.resource_class.__name__, - 'args': kwargs - } - raise exceptions.NotFound(msg) - elif num_matches > 1: - raise exceptions.NoUniqueMatch() - else: - return matches[0] - - def findall(self, **kwargs): - """Find all items with attributes matching ``**kwargs``. - - This isn't very efficient: it loads the entire list then filters on - the Python side. - """ - found = [] - searches = kwargs.items() - - for obj in self.list(): - try: - if all(getattr(obj, attr) == value - for (attr, value) in searches): - found.append(obj) - except AttributeError: - continue - - return found - - -class CrudManager(BaseManager): - """Base manager class for manipulating entities. - - Children of this class are expected to define a `collection_key` and `key`. - - - `collection_key`: Usually a plural noun by convention (e.g. `entities`); - used to refer collections in both URL's (e.g. `/v3/entities`) and JSON - objects containing a list of member resources (e.g. `{'entities': [{}, - {}, {}]}`). - - `key`: Usually a singular noun by convention (e.g. `entity`); used to - refer to an individual member of the collection. - - """ - collection_key = None - key = None - - def build_url(self, base_url=None, **kwargs): - """Builds a resource URL for the given kwargs. - - Given an example collection where `collection_key = 'entities'` and - `key = 'entity'`, the following URL's could be generated. - - By default, the URL will represent a collection of entities, e.g.:: - - /entities - - If kwargs contains an `entity_id`, then the URL will represent a - specific member, e.g.:: - - /entities/{entity_id} - - :param base_url: if provided, the generated URL will be appended to it - """ - url = base_url if base_url is not None else '' - - url += '/%s' % self.collection_key - - # do we have a specific entity? - entity_id = kwargs.get('%s_id' % self.key) - if entity_id is not None: - url += '/%s' % entity_id - - return url - - def _filter_kwargs(self, kwargs): - """Drop null values and handle ids.""" - for key, ref in kwargs.copy().items(): - if ref is None: - kwargs.pop(key) - else: - if isinstance(ref, Resource): - kwargs.pop(key) - kwargs['%s_id' % key] = getid(ref) - return kwargs - - def create(self, **kwargs): - kwargs = self._filter_kwargs(kwargs) - return self._post( - self.build_url(**kwargs), - {self.key: kwargs}, - self.key) - - def get(self, **kwargs): - kwargs = self._filter_kwargs(kwargs) - return self._get( - self.build_url(**kwargs), - self.key) - - def head(self, **kwargs): - kwargs = self._filter_kwargs(kwargs) - return self._head(self.build_url(**kwargs)) - - def list(self, base_url=None, **kwargs): - """List the collection. - - :param base_url: if provided, the generated URL will be appended to it - """ - kwargs = self._filter_kwargs(kwargs) - - return self._list( - '%(base_url)s%(query)s' % { - 'base_url': self.build_url(base_url=base_url, **kwargs), - 'query': '?%s' % parse.urlencode(kwargs) if kwargs else '', - }, - self.collection_key) - - def put(self, base_url=None, **kwargs): - """Update an element. - - :param base_url: if provided, the generated URL will be appended to it - """ - kwargs = self._filter_kwargs(kwargs) - - return self._put(self.build_url(base_url=base_url, **kwargs)) - - def update(self, **kwargs): - kwargs = self._filter_kwargs(kwargs) - params = kwargs.copy() - params.pop('%s_id' % self.key) - - return self._patch( - self.build_url(**kwargs), - {self.key: params}, - self.key) - - def delete(self, **kwargs): - kwargs = self._filter_kwargs(kwargs) - - return self._delete( - self.build_url(**kwargs)) - - def find(self, base_url=None, **kwargs): - """Find a single item with attributes matching ``**kwargs``. - - :param base_url: if provided, the generated URL will be appended to it - """ - kwargs = self._filter_kwargs(kwargs) - - rl = self._list( - '%(base_url)s%(query)s' % { - 'base_url': self.build_url(base_url=base_url, **kwargs), - 'query': '?%s' % parse.urlencode(kwargs) if kwargs else '', - }, - self.collection_key) - num = len(rl) - - if num == 0: - msg = _("No %(name)s matching %(args)s.") % { - 'name': self.resource_class.__name__, - 'args': kwargs - } - raise exceptions.NotFound(msg) - elif num > 1: - raise exceptions.NoUniqueMatch - else: - return rl[0] - - -class Extension(HookableMixin): - """Extension descriptor.""" - - SUPPORTED_HOOKS = ('__pre_parse_args__', '__post_parse_args__') - manager_class = None - - def __init__(self, name, module): - super(Extension, self).__init__() - self.name = name - self.module = module - self._parse_extension_module() - - def _parse_extension_module(self): - self.manager_class = None - for attr_name, attr_value in self.module.__dict__.items(): - if attr_name in self.SUPPORTED_HOOKS: - self.add_hook(attr_name, attr_value) - else: - try: - if issubclass(attr_value, BaseManager): - self.manager_class = attr_value - except TypeError: - pass - - def __repr__(self): - return "" % self.name - - -class Resource(object): - """Base class for OpenStack resources (tenant, user, etc.). - - This is pretty much just a bag for attributes. - """ - - HUMAN_ID = False - NAME_ATTR = 'name' - - def __init__(self, manager, info, loaded=False): - """Populate and bind to a manager. - - :param manager: BaseManager object - :param info: dictionary representing resource attributes - :param loaded: prevent lazy-loading if set to True - """ - self.manager = manager - self._info = info - self._add_details(info) - self._loaded = loaded - - def __repr__(self): - reprkeys = sorted(k - for k in self.__dict__ - if k[0] != '_' and k != 'manager') - info = ", ".join("%s=%s" % (k, getattr(self, k)) for k in reprkeys) - return "<%s %s>" % (self.__class__.__name__, info) - - @property - def human_id(self): - """Human-readable ID which can be used for bash completion. - """ - if self.HUMAN_ID: - name = getattr(self, self.NAME_ATTR, None) - if name is not None: - return strutils.to_slug(name) - return None - - def _add_details(self, info): - for (k, v) in info.items(): - try: - setattr(self, k, v) - self._info[k] = v - except AttributeError: - # In this case we already defined the attribute on the class - pass - - def __getattr__(self, k): - if k not in self.__dict__: - # NOTE(bcwaldon): disallow lazy-loading if already loaded once - if not self.is_loaded(): - self.get() - return self.__getattr__(k) - - raise AttributeError(k) - else: - return self.__dict__[k] - - def get(self): - """Support for lazy loading details. - - Some clients, such as novaclient have the option to lazy load the - details, details which can be loaded with this function. - """ - # set_loaded() first ... so if we have to bail, we know we tried. - self.set_loaded(True) - if not hasattr(self.manager, 'get'): - return - - new = self.manager.get(self.id) - if new: - self._add_details(new._info) - self._add_details( - {'x_request_id': self.manager.client.last_request_id}) - - def __eq__(self, other): - if not isinstance(other, Resource): - return NotImplemented - # two resources of different types are not equal - if not isinstance(other, self.__class__): - return False - if hasattr(self, 'id') and hasattr(other, 'id'): - return self.id == other.id - return self._info == other._info - - def is_loaded(self): - return self._loaded - - def set_loaded(self, val): - self._loaded = val - - def to_dict(self): - return copy.deepcopy(self._info) diff --git a/searchlightclient/common/exceptions.py b/searchlightclient/common/exceptions.py deleted file mode 100644 index ee99642..0000000 --- a/searchlightclient/common/exceptions.py +++ /dev/null @@ -1,477 +0,0 @@ -# Copyright 2010 Jacob Kaplan-Moss -# Copyright 2011 Nebula, Inc. -# Copyright 2013 Alessio Ababilov -# Copyright 2013 OpenStack Foundation -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -Exception definitions. -""" - -######################################################################## -# -# THIS MODULE IS DEPRECATED -# -# Please refer to -# https://etherpad.openstack.org/p/kilo-oslo-library-proposals for -# the discussion leading to this deprecation. -# -# We recommend checking out the python-openstacksdk project -# (https://launchpad.net/python-openstacksdk) instead. -# -######################################################################## - -import inspect -import sys - -from searchlightclient.i18n import _ - - -class ClientException(Exception): - """The base exception class for all exceptions this library raises. - """ - pass - - -class ValidationError(ClientException): - """Error in validation on API client side.""" - pass - - -class UnsupportedVersion(ClientException): - """User is trying to use an unsupported version of the API.""" - pass - - -class CommandError(ClientException): - """Error in CLI tool.""" - pass - - -class AuthorizationFailure(ClientException): - """Cannot authorize API client.""" - pass - - -class ConnectionError(ClientException): - """Cannot connect to API service.""" - pass - - -class ConnectionRefused(ConnectionError): - """Connection refused while trying to connect to API service.""" - pass - - -class AuthPluginOptionsMissing(AuthorizationFailure): - """Auth plugin misses some options.""" - def __init__(self, opt_names): - super(AuthPluginOptionsMissing, self).__init__( - _("Authentication failed. Missing options: %s") % - ", ".join(opt_names)) - self.opt_names = opt_names - - -class AuthSystemNotFound(AuthorizationFailure): - """User has specified an AuthSystem that is not installed.""" - def __init__(self, auth_system): - super(AuthSystemNotFound, self).__init__( - _("AuthSystemNotFound: %r") % auth_system) - self.auth_system = auth_system - - -class NoUniqueMatch(ClientException): - """Multiple entities found instead of one.""" - pass - - -class EndpointException(ClientException): - """Something is rotten in Service Catalog.""" - pass - - -class EndpointNotFound(EndpointException): - """Could not find requested endpoint in Service Catalog.""" - pass - - -class AmbiguousEndpoints(EndpointException): - """Found more than one matching endpoint in Service Catalog.""" - def __init__(self, endpoints=None): - super(AmbiguousEndpoints, self).__init__( - _("AmbiguousEndpoints: %r") % endpoints) - self.endpoints = endpoints - - -class HttpError(ClientException): - """The base exception class for all HTTP exceptions. - """ - http_status = 0 - message = _("HTTP Error") - - def __init__(self, message=None, details=None, - response=None, request_id=None, - url=None, method=None, http_status=None): - self.http_status = http_status or self.http_status - self.message = message or self.message - self.details = details - self.request_id = request_id - self.response = response - self.url = url - self.method = method - formatted_string = "%s (HTTP %s)" % (self.message, self.http_status) - if request_id: - formatted_string += " (Request-ID: %s)" % request_id - super(HttpError, self).__init__(formatted_string) - - -class HTTPRedirection(HttpError): - """HTTP Redirection.""" - message = _("HTTP Redirection") - - -class HTTPClientError(HttpError): - """Client-side HTTP error. - - Exception for cases in which the client seems to have erred. - """ - message = _("HTTP Client Error") - - -class HttpServerError(HttpError): - """Server-side HTTP error. - - Exception for cases in which the server is aware that it has - erred or is incapable of performing the request. - """ - message = _("HTTP Server Error") - - -class MultipleChoices(HTTPRedirection): - """HTTP 300 - Multiple Choices. - - Indicates multiple options for the resource that the client may follow. - """ - - http_status = 300 - message = _("Multiple Choices") - - -class BadRequest(HTTPClientError): - """HTTP 400 - Bad Request. - - The request cannot be fulfilled due to bad syntax. - """ - http_status = 400 - message = _("Bad Request") - - -class Unauthorized(HTTPClientError): - """HTTP 401 - Unauthorized. - - Similar to 403 Forbidden, but specifically for use when authentication - is required and has failed or has not yet been provided. - """ - http_status = 401 - message = _("Unauthorized") - - -class PaymentRequired(HTTPClientError): - """HTTP 402 - Payment Required. - - Reserved for future use. - """ - http_status = 402 - message = _("Payment Required") - - -class Forbidden(HTTPClientError): - """HTTP 403 - Forbidden. - - The request was a valid request, but the server is refusing to respond - to it. - """ - http_status = 403 - message = _("Forbidden") - - -class NotFound(HTTPClientError): - """HTTP 404 - Not Found. - - The requested resource could not be found but may be available again - in the future. - """ - http_status = 404 - message = _("Not Found") - - -class MethodNotAllowed(HTTPClientError): - """HTTP 405 - Method Not Allowed. - - A request was made of a resource using a request method not supported - by that resource. - """ - http_status = 405 - message = _("Method Not Allowed") - - -class NotAcceptable(HTTPClientError): - """HTTP 406 - Not Acceptable. - - The requested resource is only capable of generating content not - acceptable according to the Accept headers sent in the request. - """ - http_status = 406 - message = _("Not Acceptable") - - -class ProxyAuthenticationRequired(HTTPClientError): - """HTTP 407 - Proxy Authentication Required. - - The client must first authenticate itself with the proxy. - """ - http_status = 407 - message = _("Proxy Authentication Required") - - -class RequestTimeout(HTTPClientError): - """HTTP 408 - Request Timeout. - - The server timed out waiting for the request. - """ - http_status = 408 - message = _("Request Timeout") - - -class Conflict(HTTPClientError): - """HTTP 409 - Conflict. - - Indicates that the request could not be processed because of conflict - in the request, such as an edit conflict. - """ - http_status = 409 - message = _("Conflict") - - -class Gone(HTTPClientError): - """HTTP 410 - Gone. - - Indicates that the resource requested is no longer available and will - not be available again. - """ - http_status = 410 - message = _("Gone") - - -class LengthRequired(HTTPClientError): - """HTTP 411 - Length Required. - - The request did not specify the length of its content, which is - required by the requested resource. - """ - http_status = 411 - message = _("Length Required") - - -class PreconditionFailed(HTTPClientError): - """HTTP 412 - Precondition Failed. - - The server does not meet one of the preconditions that the requester - put on the request. - """ - http_status = 412 - message = _("Precondition Failed") - - -class RequestEntityTooLarge(HTTPClientError): - """HTTP 413 - Request Entity Too Large. - - The request is larger than the server is willing or able to process. - """ - http_status = 413 - message = _("Request Entity Too Large") - - def __init__(self, *args, **kwargs): - try: - self.retry_after = int(kwargs.pop('retry_after')) - except (KeyError, ValueError): - self.retry_after = 0 - - super(RequestEntityTooLarge, self).__init__(*args, **kwargs) - - -class RequestUriTooLong(HTTPClientError): - """HTTP 414 - Request-URI Too Long. - - The URI provided was too long for the server to process. - """ - http_status = 414 - message = _("Request-URI Too Long") - - -class UnsupportedMediaType(HTTPClientError): - """HTTP 415 - Unsupported Media Type. - - The request entity has a media type which the server or resource does - not support. - """ - http_status = 415 - message = _("Unsupported Media Type") - - -class RequestedRangeNotSatisfiable(HTTPClientError): - """HTTP 416 - Requested Range Not Satisfiable. - - The client has asked for a portion of the file, but the server cannot - supply that portion. - """ - http_status = 416 - message = _("Requested Range Not Satisfiable") - - -class ExpectationFailed(HTTPClientError): - """HTTP 417 - Expectation Failed. - - The server cannot meet the requirements of the Expect request-header field. - """ - http_status = 417 - message = _("Expectation Failed") - - -class UnprocessableEntity(HTTPClientError): - """HTTP 422 - Unprocessable Entity. - - The request was well-formed but was unable to be followed due to semantic - errors. - """ - http_status = 422 - message = _("Unprocessable Entity") - - -class InternalServerError(HttpServerError): - """HTTP 500 - Internal Server Error. - - A generic error message, given when no more specific message is suitable. - """ - http_status = 500 - message = _("Internal Server Error") - - -# NotImplemented is a python keyword. -class HttpNotImplemented(HttpServerError): - """HTTP 501 - Not Implemented. - - The server either does not recognize the request method, or it lacks - the ability to fulfill the request. - """ - http_status = 501 - message = _("Not Implemented") - - -class BadGateway(HttpServerError): - """HTTP 502 - Bad Gateway. - - The server was acting as a gateway or proxy and received an invalid - response from the upstream server. - """ - http_status = 502 - message = _("Bad Gateway") - - -class ServiceUnavailable(HttpServerError): - """HTTP 503 - Service Unavailable. - - The server is currently unavailable. - """ - http_status = 503 - message = _("Service Unavailable") - - -class GatewayTimeout(HttpServerError): - """HTTP 504 - Gateway Timeout. - - The server was acting as a gateway or proxy and did not receive a timely - response from the upstream server. - """ - http_status = 504 - message = _("Gateway Timeout") - - -class HttpVersionNotSupported(HttpServerError): - """HTTP 505 - HttpVersion Not Supported. - - The server does not support the HTTP protocol version used in the request. - """ - http_status = 505 - message = _("HTTP Version Not Supported") - - -# _code_map contains all the classes that have http_status attribute. -_code_map = dict( - (getattr(obj, 'http_status', None), obj) - for name, obj in vars(sys.modules[__name__]).items() - if inspect.isclass(obj) and getattr(obj, 'http_status', False) -) - - -def from_response(response, method, url): - """Returns an instance of :class:`HttpError` or subclass based on response. - - :param response: instance of `requests.Response` class - :param method: HTTP method used for request - :param url: URL used for request - """ - - req_id = response.headers.get("x-openstack-request-id") - # NOTE(hdd) true for older versions of nova and cinder - if not req_id: - req_id = response.headers.get("x-compute-request-id") - kwargs = { - "http_status": response.status_code, - "response": response, - "method": method, - "url": url, - "request_id": req_id, - } - if "retry-after" in response.headers: - kwargs["retry_after"] = response.headers["retry-after"] - - content_type = response.headers.get("Content-Type", "") - if content_type.startswith("application/json"): - try: - body = response.json() - except ValueError: - pass - else: - if isinstance(body, dict): - error = body.get(list(body)[0]) - if isinstance(error, dict): - kwargs["message"] = (error.get("message") or - error.get("faultstring")) - kwargs["details"] = (error.get("details") or - str(body)) - elif content_type.startswith("text/"): - kwargs["details"] = getattr(response, 'text', '') - - try: - cls = _code_map[response.status_code] - except KeyError: - if 500 <= response.status_code < 600: - cls = HttpServerError - elif 400 <= response.status_code < 500: - cls = HTTPClientError - else: - cls = HttpError - return cls(**kwargs) diff --git a/searchlightclient/common/utils.py b/searchlightclient/common/utils.py deleted file mode 100644 index c2d000c..0000000 --- a/searchlightclient/common/utils.py +++ /dev/null @@ -1,313 +0,0 @@ -# Copyright 2012 OpenStack Foundation -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import base64 -import logging -import os -import textwrap -import uuid - -from oslo_serialization import jsonutils -from oslo_utils import encodeutils -from oslo_utils import importutils -import prettytable -from urllib import error -from urllib import parse -from urllib import request -import yaml - -from searchlightclient import exc -from searchlightclient.i18n import _ - - -LOG = logging.getLogger(__name__) - - -supported_formats = { - "json": lambda x: jsonutils.dumps(x, indent=2), - "yaml": yaml.safe_dump -} - - -def env(*args, **kwargs): - """Returns the first environment variable set. - - If all are empty, defaults to '' or keyword arg `default`. - """ - for arg in args: - value = os.environ.get(arg) - if value: - return value - return kwargs.get('default', '') - - -def link_formatter(links): - def format_link(l): - if 'rel' in l: - return "%s (%s)" % (l.get('href', ''), l.get('rel', '')) - else: - return "%s" % (l.get('href', '')) - return '\n'.join(format_link(l) for l in links or []) - - -def resource_nested_identifier(rsrc): - nested_link = [l for l in rsrc.links or [] - if l.get('rel') == 'nested'] - if nested_link: - nested_href = nested_link[0].get('href') - nested_identifier = nested_href.split("/")[-2:] - return "/".join(nested_identifier) - - -def json_formatter(js): - return jsonutils.dumps(js, indent=2, ensure_ascii=False, - separators=(', ', ': ')) - - -def yaml_formatter(js): - return yaml.safe_dump(js, default_flow_style=False) - - -def text_wrap_formatter(d): - return '\n'.join(textwrap.wrap(d or '', 55)) - - -def newline_list_formatter(r): - return '\n'.join(r or []) - - -def print_dict(d, formatters=None): - formatters = formatters or {} - pt = prettytable.PrettyTable(['Property', 'Value'], - caching=False, print_empty=False) - pt.align = 'l' - - for field in d: - if field in formatters: - pt.add_row([field, formatters[field](d[field])]) - else: - pt.add_row([field, d[field]]) - print(pt.get_string(sortby='Property')) - - -def event_log_formatter(events): - """Return the events in log format.""" - event_log = [] - log_format = _("%(event_date)s %(event_time)s %(event_id)s " - "[%(rsrc_name)s]: %(rsrc_status)s %(rsrc_status_reason)s") - for event in events: - event_time = getattr(event, 'event_time', '') - time_date = event_time.split('T') - try: - event_date = time_date[0] - event_time = time_date[1] - except IndexError: - event_time = event_date = '' - - log = log_format % { - 'event_date': event_date, 'event_time': event_time, - 'event_id': getattr(event, 'id', ''), - 'rsrc_name': getattr(event, 'resource_name', ''), - 'rsrc_status': getattr(event, 'resource_status', ''), - 'rsrc_status_reason': getattr(event, 'resource_status_reason', '') - } - event_log.append(log) - - return "\n".join(event_log) - - -def print_update_list(lst, fields, formatters=None): - """Print the stack-update --dry-run output as a table. - - This function is necessary to print the stack-update --dry-run - output, which contains additional information about the update. - """ - formatters = formatters or {} - pt = prettytable.PrettyTable(fields, caching=False, print_empty=False) - pt.align = 'l' - - for change in lst: - row = [] - for field in fields: - if field in formatters: - row.append(formatters[field](change.get(field, None))) - else: - row.append(change.get(field, None)) - - pt.add_row(row) - - print(encodeutils.safe_encode(pt.get_string()).decode()) - - -def find_resource(manager, name_or_id): - """Helper for the _find_* methods.""" - # first try to get entity as integer id - try: - if isinstance(name_or_id, int) or name_or_id.isdigit(): - return manager.get(int(name_or_id)) - except exc.NotFound: - pass - - # now try to get entity as uuid - try: - uuid.UUID(str(name_or_id)) - return manager.get(name_or_id) - except (ValueError, exc.NotFound): - pass - - # finally try to find entity by name - try: - return manager.find(name=name_or_id) - except exc.NotFound: - msg = _("No %(name)s with a name or ID of " - "'%(name_or_id)s' exists.") % \ - { - 'name': manager.resource_class.__name__.lower(), - 'name_or_id': name_or_id} - raise exc.CommandError(msg) - - -def import_versioned_module(version, submodule=None): - module = 'searchlightclient.v%s' % version - if submodule: - module = '.'.join((module, submodule)) - return importutils.import_module(module) - - -def format_parameters(params, parse_semicolon=True): - '''Reformat parameters into dict of format expected by the API.''' - - if not params: - return {} - - if parse_semicolon: - # expect multiple invocations of --parameters but fall back - # to ; delimited if only one --parameters is specified - if len(params) == 1: - params = params[0].split(';') - - parameters = {} - for p in params: - try: - (n, v) = p.split(('='), 1) - except ValueError: - msg = _('Malformed parameter(%s). Use the key=value format.') % p - raise exc.CommandError(msg) - - if n not in parameters: - parameters[n] = v - else: - if not isinstance(parameters[n], list): - parameters[n] = [parameters[n]] - parameters[n].append(v) - - return parameters - - -def format_all_parameters(params, param_files, - template_file=None, template_url=None): - parameters = {} - parameters.update(format_parameters(params)) - parameters.update(format_parameter_file( - param_files, - template_file, - template_url)) - return parameters - - -def format_parameter_file(param_files, template_file=None, - template_url=None): - '''Reformat file parameters into dict of format expected by the API.''' - if not param_files: - return {} - params = format_parameters(param_files, False) - - template_base_url = None - if template_file or template_url: - template_base_url = base_url_for_url(get_template_url( - template_file, template_url)) - - param_file = {} - for key, value in iter(params.items()): - param_file[key] = resolve_param_get_file(value, - template_base_url) - return param_file - - -def resolve_param_get_file(file, base_url): - if base_url and not base_url.endswith('/'): - base_url = base_url + '/' - str_url = parse.urljoin(base_url, file) - return read_url_content(str_url) - - -def format_output(output, format='yaml'): - """Format the supplied dict as specified.""" - output_format = format.lower() - try: - return supported_formats[output_format](output) - except KeyError: - raise exc.HTTPUnsupported(_("The format(%s) is unsupported.") - % output_format) - - -def parse_query_url(url): - base_url, query_params = url.split('?') - return base_url, parse.parse_qs(query_params) - - -def get_template_url(template_file=None, template_url=None): - if template_file: - template_url = normalise_file_path_to_url(template_file) - return template_url - - -def read_url_content(url): - try: - content = request.urlopen(url).read() - except error.URLError: - raise exc.CommandError(_('Could not fetch contents for %s') % url) - - if content: - try: - content.decode('utf-8') - except ValueError: - content = base64.encodestring(content) - return content - - -def base_url_for_url(url): - parsed = parse.urlparse(url) - parsed_dir = os.path.dirname(parsed.path) - return parse.urljoin(url, parsed_dir) - - -def normalise_file_path_to_url(path): - if parse.urlparse(path).scheme: - return path - path = os.path.abspath(path) - return parse.urljoin('file:', request.pathname2url(path)) - - -def get_response_body(resp): - body = resp.content - if 'application/json' in resp.headers.get('content-type', ''): - try: - body = resp.json() - except ValueError: - LOG.error('Could not decode response body as JSON') - else: - body = None - return body diff --git a/searchlightclient/exc.py b/searchlightclient/exc.py deleted file mode 100644 index 9197fb8..0000000 --- a/searchlightclient/exc.py +++ /dev/null @@ -1,191 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import sys - -from oslo_serialization import jsonutils - -from searchlightclient.i18n import _ - -verbose = 0 - - -class BaseException(Exception): - """An error occurred.""" - def __init__(self, message=None): - self.message = message - - def __str__(self): - return self.message or self.__class__.__doc__ - - -class CommandError(BaseException): - """Invalid usage of CLI.""" - - -class InvalidEndpoint(BaseException): - """The provided endpoint is invalid.""" - - -class CommunicationError(BaseException): - """Unable to communicate with server.""" - - -class HTTPException(BaseException): - """Base exception for all HTTP-derived exceptions.""" - code = 'N/A' - - def __init__(self, message=None): - super(HTTPException, self).__init__(message) - try: - self.error = jsonutils.loads(message) - if 'error' not in self.error: - raise KeyError(_('Key "error" not exists')) - except KeyError: - # NOTE(jianingy): If key 'error' happens not exist, - # self.message becomes no sense. In this case, we - # return doc of current exception class instead. - self.error = {'error': - {'message': self.__class__.__doc__}} - except Exception: - self.error = {'error': - {'message': self.message or self.__class__.__doc__}} - - def __str__(self): - message = self.error['error'].get('message', 'Internal Error') - if verbose: - traceback = self.error['error'].get('traceback', '') - return (_('ERROR: %(message)s\n%(traceback)s') % - {'message': message, 'traceback': traceback}) - else: - return _('ERROR: %s') % message - - -class HTTPMultipleChoices(HTTPException): - code = 300 - - def __str__(self): - self.details = _("Requested version of Searchlight API is not" - "available.") - return (_("%(name)s (HTTP %(code)s) %(details)s") % - { - 'name': self.__class__.__name__, - 'code': self.code, - 'details': self.details}) - - -class BadRequest(HTTPException): - """DEPRECATED.""" - code = 400 - - -class HTTPBadRequest(BadRequest): - pass - - -class Unauthorized(HTTPException): - """DEPRECATED.""" - code = 401 - - -class HTTPUnauthorized(Unauthorized): - pass - - -class Forbidden(HTTPException): - """DEPRECATED.""" - code = 403 - - -class HTTPForbidden(Forbidden): - pass - - -class NotFound(HTTPException): - """DEPRECATED.""" - code = 404 - - -class HTTPNotFound(NotFound): - pass - - -class HTTPMethodNotAllowed(HTTPException): - code = 405 - - -class Conflict(HTTPException): - """DEPRECATED.""" - code = 409 - - -class HTTPConflict(Conflict): - pass - - -class OverLimit(HTTPException): - """DEPRECATED.""" - code = 413 - - -class HTTPOverLimit(OverLimit): - pass - - -class HTTPUnsupported(HTTPException): - code = 415 - - -class HTTPInternalServerError(HTTPException): - code = 500 - - -class HTTPNotImplemented(HTTPException): - code = 501 - - -class HTTPBadGateway(HTTPException): - code = 502 - - -class ServiceUnavailable(HTTPException): - """DEPRECATED.""" - code = 503 - - -class HTTPServiceUnavailable(ServiceUnavailable): - pass - - -# NOTE(bcwaldon): Build a mapping of HTTP codes to corresponding exception -# classes -_code_map = {} -for obj_name in dir(sys.modules[__name__]): - if obj_name.startswith('HTTP'): - obj = getattr(sys.modules[__name__], obj_name) - _code_map[obj.code] = obj - - -def from_response(response): - """Return an instance of an HTTPException based on requests response.""" - cls = _code_map.get(response.status_code, HTTPException) - return cls(response.content) - - -class NoTokenLookupException(Exception): - """DEPRECATED.""" - pass - - -class EndpointNotFound(Exception): - """DEPRECATED.""" - pass diff --git a/searchlightclient/i18n.py b/searchlightclient/i18n.py deleted file mode 100644 index 104a6a0..0000000 --- a/searchlightclient/i18n.py +++ /dev/null @@ -1,25 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -"""oslo_i18n integration module for searchlightclient. - -See https://docs.openstack.org/oslo.i18n/latest/user/index.html. - -""" - -import oslo_i18n - - -_translators = oslo_i18n.TranslatorFactory(domain='searchlightclient') - -# The primary translation function using the well-known name "_" -_ = _translators.primary diff --git a/searchlightclient/osc/__init__.py b/searchlightclient/osc/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/searchlightclient/osc/plugin.py b/searchlightclient/osc/plugin.py deleted file mode 100644 index a3437ce..0000000 --- a/searchlightclient/osc/plugin.py +++ /dev/null @@ -1,61 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -"""OpenStackClient plugin for Search service.""" - -import logging - -from osc_lib import utils - -DEFAULT_SEARCH_API_VERSION = '1' -API_VERSION_OPTION = 'os_search_api_version' -API_NAME = 'search' -API_VERSIONS = { - '1': 'searchlightclient.v1.client.Client', -} - - -def make_client(instance): - """Returns a search service client""" - search_client = utils.get_client_class( - API_NAME, - instance._api_version[API_NAME], - API_VERSIONS) - - # Set client http_log_debug to True if verbosity level is high enough - http_log_debug = utils.get_effective_log_level() <= logging.DEBUG - - # Remember interface only if it is set - kwargs = utils.build_kwargs_dict('endpoint_type', instance._interface) - client = search_client( - session=instance.session, - http_log_debug=http_log_debug, - region_name=instance._region_name, - **kwargs - ) - - return client - - -def build_option_parser(parser): - """Hook to add global options""" - parser.add_argument( - '--os-search-api-version', - metavar='', - default=utils.env( - 'OS_SEARCH_API_VERSION', - default=DEFAULT_SEARCH_API_VERSION), - help='Search API version, default=' + - DEFAULT_SEARCH_API_VERSION + - ' (Env: OS_SEARCH_API_VERSION)') - return parser diff --git a/searchlightclient/osc/v1/__init__.py b/searchlightclient/osc/v1/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/searchlightclient/osc/v1/facet.py b/searchlightclient/osc/v1/facet.py deleted file mode 100644 index d69f986..0000000 --- a/searchlightclient/osc/v1/facet.py +++ /dev/null @@ -1,79 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -"""Searchlight v1 Facet action implementations""" - -import logging - -from osc_lib.command import command -from osc_lib import utils - - -class ListFacet(command.Lister): - """List Searchlight Facet.""" - - log = logging.getLogger(__name__ + ".ListFacet") - - def get_parser(self, prog_name): - parser = super(ListFacet, self).get_parser(prog_name) - parser.add_argument( - "--type", - metavar="", - help="Get facets for a particular resource type" - ) - parser.add_argument( - "--limit-terms", - metavar="", - help="Restricts the number of options returned for " - "fields that support facet terms" - ) - parser.add_argument( - "--all-projects", - action='store_true', - default=False, - help="Request facet terms for all projects (admin only)" - ) - return parser - - def take_action(self, parsed_args): - self.log.debug("take_action(%s)", parsed_args) - - search_client = self.app.client_manager.search - columns = ( - "Resource Type", - "Type", - "Name", - "Options" - ) - params = { - "type": parsed_args.type, - "limit_terms": parsed_args.limit_terms, - "all_projects": parsed_args.all_projects - } - data = search_client.facets.list(**params) - result = [] - for resource_type, values in data.items(): - if isinstance(values, list): - # Cope with pre-1.0 service APIs - facets = values - else: - facets = values['facets'] - for s in facets: - options = [] - for o in s.get('options', []): - options.append( - str(o['key']) + '(' + str(o['doc_count']) + ')') - s["options"] = ', '.join(options) - s["resource_type"] = resource_type - result.append(utils.get_dict_properties(s, columns)) - return (columns, result) diff --git a/searchlightclient/osc/v1/resource_type.py b/searchlightclient/osc/v1/resource_type.py deleted file mode 100644 index ba42b82..0000000 --- a/searchlightclient/osc/v1/resource_type.py +++ /dev/null @@ -1,49 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -"""Searchlight v1 Resource Type action implementations""" - -import logging - -from osc_lib.command import command - - -class ListResourceType(command.Lister): - """List Searchlight Resource Type (Plugin).""" - - log = logging.getLogger(__name__ + ".ListResourceType") - - def take_action(self, parsed_args): - self.log.debug("take_action(%s)", parsed_args) - - search_client = self.app.client_manager.search - columns = ( - "Alias Searching", - "Alias Indexing", - "Type" - ) - data = search_client.resource_types.list() - return (columns, - (self.get_item_properties( - s, columns, - ) for s in data)) - - def get_item_properties(self, item, fields): - # osc_lib.utils.get_item_properties doesn't work because - # the field names are using "-" instead of "_". - row = [] - for field in fields: - field_name = field.lower().replace(' ', '-') - data = getattr(item, field_name, '') - row.append(data) - return tuple(row) diff --git a/searchlightclient/osc/v1/search.py b/searchlightclient/osc/v1/search.py deleted file mode 100644 index 447da5e..0000000 --- a/searchlightclient/osc/v1/search.py +++ /dev/null @@ -1,136 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -"""Searchlight v1 Search action implementations""" - -import logging - -from oslo_serialization import jsonutils - -from osc_lib.command import command -from osc_lib import utils - - -class SearchResource(command.Lister): - """Search Searchlight resource.""" - - log = logging.getLogger(__name__ + ".SearchResource") - - def get_parser(self, prog_name): - parser = super(SearchResource, self).get_parser(prog_name) - parser.add_argument( - "query", - metavar="", - help="Query resources by Elasticsearch query string or json " - "format DSL. Query string example: 'name: cirros AND " - "updated_at: [now-1y TO now]'. DSL example: " - "'{\"term\": {\"name\": \"cirros\"}}'. " - "See Elasticsearch DSL or Searchlight documentation for " - "more detail." - ) - parser.add_argument( - "--json", - action='store_true', - default=False, - help="Treat the query argument as a JSON formatted DSL query." - ) - parser.add_argument( - "--type", - nargs='*', - metavar="", - help="One or more types to search. Uniquely identifies resource " - "types. Example: --type OS::Glance::Image " - "OS::Nova::Server" - ) - parser.add_argument( - "--all-projects", - action='store_true', - default=False, - help="By default searches are restricted to the current project " - "unless all_projects is set" - ) - parser.add_argument( - "--source", - nargs='?', - const='all_sources', - metavar="[,...]", - help="Whether to display the json source. If not specified, " - "it will not be displayed. If specified with no argument, " - "the full source will be displayed. Otherwise, specify the " - "fields combined with ',' to return the fields you want. " - "It is recommended that you use the --max-width argument " - "with this option." - ) - return parser - - def take_action(self, parsed_args): - self.log.debug("take_action(%s)", parsed_args) - - search_client = self.app.client_manager.search - mapping = {"_score": "score", "_type": "type", "_id": "id", - "_index": "index", "_source": "source"} - - params = { - "type": parsed_args.type, - "all_projects": parsed_args.all_projects - } - source = parsed_args.source - if source: - columns = ("ID", "Score", "Type", "Source") - if source != "all_sources": - params["_source"] = (["id"] + - [s for s in source.split(",") if s != 'id']) - else: - columns = ("ID", "Name", "Score", "Type", "Updated") - # Only return the required fields when source not specified. - params["_source"] = ["id", "name", "updated_at"] - - if parsed_args.query: - if parsed_args.json: - query = jsonutils.loads(parsed_args.query) - else: - try: - jsonutils.loads(parsed_args.query) - print("You should use the --json flag when specifying " - "a JSON object.") - exit(1) - except Exception: - qs = self._modify_query_string(parsed_args.query) - query = {"query_string": {"query": qs}} - - params['query'] = query - - data = search_client.search.search(**params) - result = [] - for r in data.hits['hits']: - converted = {} - extra = {} - # hit._id may include extra information appended after _, - # so use r['_source']['id'] for safe. - r['_id'] = r.get('_source', {}).get('id') - for k, v in r.items(): - map_key = mapping.get(k) - if map_key is not None: - converted[map_key] = v - if k == "_source" and not parsed_args.source: - converted["name"] = v.get("name") - converted["updated"] = v.get("updated_at") - else: - extra[k] = v - if extra: - self.log.debug("extra info returned: %s", extra) - result.append(utils.get_dict_properties(converted, columns)) - return (columns, result) - - def _modify_query_string(self, query_string): - return query_string.replace(r'/', r'\/') diff --git a/searchlightclient/tests/__init__.py b/searchlightclient/tests/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/searchlightclient/tests/unit/__init__.py b/searchlightclient/tests/unit/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/searchlightclient/tests/unit/osc/__init__.py b/searchlightclient/tests/unit/osc/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/searchlightclient/tests/unit/osc/fakes.py b/searchlightclient/tests/unit/osc/fakes.py deleted file mode 100644 index bfa6ea4..0000000 --- a/searchlightclient/tests/unit/osc/fakes.py +++ /dev/null @@ -1,67 +0,0 @@ -# Copyright 2013 Nebula 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 - - -AUTH_TOKEN = "foobar" -AUTH_URL = "http://0.0.0.0" - - -class FakeStdout: - def __init__(self): - self.content = [] - - def write(self, text): - self.content.append(text) - - def make_string(self): - result = '' - for line in self.content: - result = result + line - return result - - -class FakeApp(object): - def __init__(self, _stdout): - self.stdout = _stdout - self.client_manager = None - self.stdin = sys.stdin - self.stdout = _stdout or sys.stdout - self.stderr = sys.stderr - - -class FakeClientManager(object): - def __init__(self): - self.session = None - self.auth_ref = None - - -class FakeResource(object): - def __init__(self, manager, info, loaded=False): - self.manager = manager - self._info = info - self._add_details(info) - self._loaded = loaded - - def _add_details(self, info): - for (k, v) in info.items(): - setattr(self, k, v) - - def __repr__(self): - reprkeys = sorted(k for k in self.__dict__ if k[0] != '_' and - k != 'manager') - info = ", ".join("%s=%s" % (k, getattr(self, k)) for k in reprkeys) - return "<%s %s>" % (self.__class__.__name__, info) diff --git a/searchlightclient/tests/unit/osc/utils.py b/searchlightclient/tests/unit/osc/utils.py deleted file mode 100644 index 6c3c6b4..0000000 --- a/searchlightclient/tests/unit/osc/utils.py +++ /dev/null @@ -1,93 +0,0 @@ -# Copyright 2012-2013 OpenStack Foundation -# Copyright 2013 Nebula Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -import os - -import fixtures -import sys -import testtools - -from searchlightclient.tests.unit.osc import fakes - - -class TestCase(testtools.TestCase): - def setUp(self): - testtools.TestCase.setUp(self) - - if (os.environ.get("OS_STDOUT_CAPTURE") == "True" or - os.environ.get("OS_STDOUT_CAPTURE") == "1"): - stdout = self.useFixture(fixtures.StringStream("stdout")).stream - self.useFixture(fixtures.MonkeyPatch("sys.stdout", stdout)) - - if (os.environ.get("OS_STDERR_CAPTURE") == "True" or - os.environ.get("OS_STDERR_CAPTURE") == "1"): - stderr = self.useFixture(fixtures.StringStream("stderr")).stream - self.useFixture(fixtures.MonkeyPatch("sys.stderr", stderr)) - - def assertNotCalled(self, m, msg=None): - """Assert a function was not called""" - - if m.called: - if not msg: - msg = 'method %s should not have been called' % m - self.fail(msg) - - # 2.6 doesn't have the assert dict equals so make sure that it exists - if tuple(sys.version_info)[0:2] < (2, 7): - - def assertIsInstance(self, obj, cls, msg=None): - """self.assertTrue(isinstance(obj, cls)), with a nicer message""" - - if not isinstance(obj, cls): - standardMsg = '%s is not an instance of %r' % (obj, cls) - self.fail(self._formatMessage(msg, standardMsg)) - - def assertDictEqual(self, d1, d2, msg=None): - # Simple version taken from 2.7 - self.assertIsInstance(d1, dict, - 'First argument is not a dictionary') - self.assertIsInstance(d2, dict, - 'Second argument is not a dictionary') - if d1 != d2: - if msg: - self.fail(msg) - else: - standardMsg = '%r != %r' % (d1, d2) - self.fail(standardMsg) - - -class TestCommand(TestCase): - """Test osc_lib command classes""" - - def setUp(self): - super(TestCommand, self).setUp() - # Build up a fake app - self.fake_stdout = fakes.FakeStdout() - self.app = fakes.FakeApp(self.fake_stdout) - self.app.client_manager = fakes.FakeClientManager() - - def check_parser(self, cmd, args, verify_args): - cmd_parser = cmd.get_parser('check_parser') - try: - parsed_args = cmd_parser.parse_args(args) - except SystemExit: - raise Exception("Argument parse failed") - for av in verify_args: - attr, value = av - if attr: - self.assertIn(attr, parsed_args) - self.assertEqual(getattr(parsed_args, attr), value) - return parsed_args diff --git a/searchlightclient/tests/unit/osc/v1/__init__.py b/searchlightclient/tests/unit/osc/v1/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/searchlightclient/tests/unit/osc/v1/fakes.py b/searchlightclient/tests/unit/osc/v1/fakes.py deleted file mode 100644 index badab17..0000000 --- a/searchlightclient/tests/unit/osc/v1/fakes.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright 2014 OpenStack Foundation -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -from unittest import mock - -from searchlightclient.tests.unit.osc import fakes -from searchlightclient.tests.unit.osc import utils - - -ResourceType = { - "alias-searching": "searchlight-search", - "alias-indexing": "searchlight-listener", - "type": "OS::Nova::Server" -} - - -OldFacet = { - "OS::Nova::Server": - [ - {"type": "string", "name": "id"}, - {"type": "date", "name": "created_at"}, - ], -} - - -# The alternate facet format introduced in -# https://blueprints.launchpad.net/searchlight/+spec/count-endpoint -Facet = { - "OS::Nova::Server": - { - "doc_count": 2, - "facets": [ - {"type": "string", "name": "id"}, - {"type": "date", "name": "created_at"}, - ] - } -} - - -Resource = { - "hits": - {"hits": - [ - {"_score": 0.3, "_type": "OS::Glance::Image", "_id": "1", - "_source": {"id": "1", "name": "image1", - "updated_at": "2016-01-01T00:00:00Z"}}, - {"_score": 0.3, "_type": "OS::Nova::Server", "_id": "2_ADMIN", - "_source": {"id": "2", "name": "instance1", - "updated_at": "2016-01-01T00:00:00Z"}}, - ], - "_shards": {"successful": 5, "failed": 0, "total": 5}, - "took": 5, "timed_out": False - } -} - - -class FakeSearchv1Client(object): - def __init__(self, **kwargs): - self.http_client = mock.Mock() - self.http_client.auth_token = kwargs['token'] - self.http_client.management_url = kwargs['endpoint'] - self.resource_types = mock.Mock() - self.resource_types.list = mock.Mock(return_value=[]) - self.facets = mock.Mock() - self.facets.list = mock.Mock(return_value=[]) - self.search = mock.Mock() - self.search.search = mock.Mock(return_value=[]) - - -class TestSearchv1(utils.TestCommand): - def setUp(self): - super(TestSearchv1, self).setUp() - - self.app.client_manager.search = FakeSearchv1Client( - endpoint=fakes.AUTH_URL, - token=fakes.AUTH_TOKEN, - ) diff --git a/searchlightclient/tests/unit/osc/v1/test_facet.py b/searchlightclient/tests/unit/osc/v1/test_facet.py deleted file mode 100644 index d8c1f2f..0000000 --- a/searchlightclient/tests/unit/osc/v1/test_facet.py +++ /dev/null @@ -1,70 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -from searchlightclient.osc.v1 import facet -from searchlightclient.tests.unit.osc.v1 import fakes as searchlight_fakes - - -class TestFacet(searchlight_fakes.TestSearchv1): - def setUp(self): - super(TestFacet, self).setUp() - self.facet_client = self.app.client_manager.search.facets - - -class TestFacetListBase(TestFacet): - def setUp(self): - super(TestFacetListBase, self).setUp() - self.cmd = facet.ListFacet(self.app, None) - self.facet_client.list.return_value = searchlight_fakes.Facet - - def _test_list(self, arglist, **assertArgs): - parsed_args = self.check_parser(self.cmd, arglist, []) - columns, data = self.cmd.take_action(parsed_args) - self.facet_client.list.assert_called_with(**assertArgs) - - collist = ('Resource Type', 'Type', 'Name', 'Options') - self.assertEqual(collist, columns) - - datalist = ( - ('OS::Nova::Server', 'string', 'id', ''), - ('OS::Nova::Server', 'date', 'created_at', ''), - ) - self.assertEqual(datalist, tuple(data)) - - -class TestFacetList(TestFacetListBase): - def test_list(self): - self._test_list([], all_projects=False, limit_terms=None, type=None) - - def test_list_all_projects(self): - self._test_list(['--all-projects'], - all_projects=True, limit_terms=None, type=None) - - def test_list_with_type(self): - self._test_list(['--type', 'fake_res_type'], - all_projects=False, - limit_terms=None, type='fake_res_type') - - def test_list_with_limit_terms(self): - self._test_list(['--limit-terms', 'fake_limit'], - all_projects=False, - limit_terms='fake_limit', type=None) - - -class TestOldFacetList(TestFacetListBase): - def setUp(self): - super(TestOldFacetList, self).setUp() - self.facet_client.list.return_value = searchlight_fakes.OldFacet - - def test_list(self): - self._test_list([], all_projects=False, limit_terms=None, type=None) diff --git a/searchlightclient/tests/unit/osc/v1/test_resource_type.py b/searchlightclient/tests/unit/osc/v1/test_resource_type.py deleted file mode 100644 index f22e4b5..0000000 --- a/searchlightclient/tests/unit/osc/v1/test_resource_type.py +++ /dev/null @@ -1,51 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -import copy - -from searchlightclient.osc.v1 import resource_type -from searchlightclient.tests.unit.osc import fakes -from searchlightclient.tests.unit.osc.v1 import fakes as searchlight_fakes - - -class TestResourceType(searchlight_fakes.TestSearchv1): - def setUp(self): - super(TestResourceType, self).setUp() - self.rtype_client = self.app.client_manager.search.resource_types - - -class TestResourceTypeList(TestResourceType): - - def setUp(self): - super(TestResourceTypeList, self).setUp() - self.cmd = resource_type.ListResourceType(self.app, None) - self.rtype_client.list.return_value = [ - fakes.FakeResource( - None, - copy.deepcopy(searchlight_fakes.ResourceType), - loaded=True, - ), - ] - - def test_list(self): - parsed_args = self.check_parser(self.cmd, [], []) - columns, data = self.cmd.take_action(parsed_args) - self.rtype_client.list.assert_called_with() - - collist = ('Alias Searching', 'Alias Indexing', 'Type') - self.assertEqual(collist, columns) - - datalist = (('searchlight-search', - 'searchlight-listener', - 'OS::Nova::Server'),) - self.assertEqual(datalist, tuple(data)) diff --git a/searchlightclient/tests/unit/osc/v1/test_search.py b/searchlightclient/tests/unit/osc/v1/test_search.py deleted file mode 100644 index e8b2ceb..0000000 --- a/searchlightclient/tests/unit/osc/v1/test_search.py +++ /dev/null @@ -1,133 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -import copy - -from searchlightclient.osc.v1 import search -from searchlightclient.tests.unit.osc import fakes -from searchlightclient.tests.unit.osc.v1 import fakes as searchlight_fakes - - -class TestSearch(searchlight_fakes.TestSearchv1): - def setUp(self): - super(TestSearch, self).setUp() - self.search_client = self.app.client_manager.search.search - - -class TestSearchResource(TestSearch): - - def setUp(self): - super(TestSearchResource, self).setUp() - self.cmd = search.SearchResource(self.app, None) - fake_data = copy.deepcopy(searchlight_fakes.Resource) - fake_data['hits']['hits'][0]['is_not_processed'] = 'foo' - self.search_client.search.return_value = \ - fakes.FakeResource(None, fake_data, loaded=True) - - def _test_search(self, arglist, **assertArgs): - parsed_args = self.check_parser(self.cmd, arglist, []) - columns, data = self.cmd.take_action(parsed_args) - details = False - if assertArgs.get("source"): - details = True - if assertArgs.get("source") == "all_resources": - assertArgs.pop("source") - else: - assertArgs["_source"] = assertArgs.pop("source") - self.search_client.search.assert_called_with(**assertArgs) - - if details: - collist = ("ID", "Score", "Type", "Source") - datalist = ( - ('1', 0.3, 'OS::Glance::Image', - {'id': '1', 'name': 'image1', - 'updated_at': '2016-01-01T00:00:00Z'}), - ('2', 0.3, 'OS::Nova::Server', - {'id': '2', 'name': 'instance1', - 'updated_at': '2016-01-01T00:00:00Z'})) - else: - collist = ("ID", "Name", "Score", "Type", "Updated") - datalist = (('1', 'image1', 0.3, 'OS::Glance::Image', - '2016-01-01T00:00:00Z'), - ('2', 'instance1', 0.3, 'OS::Nova::Server', - '2016-01-01T00:00:00Z')) - - self.assertEqual(collist, columns) - self.assertEqual(datalist, tuple(data)) - - def test_search(self): - self._test_search(["name: fake"], - query={"query_string": {"query": "name: fake"}}, - _source=['id', 'name', 'updated_at'], - all_projects=False, type=None) - - def test_search_resource(self): - self._test_search(["name: fake", "--type", "res1", "res2"], - query={"query_string": {"query": "name: fake"}}, - _source=['id', 'name', 'updated_at'], - type=["res1", "res2"], - all_projects=False) - - def test_search_query_string(self): - self._test_search(["name: fake"], - query={"query_string": {"query": "name: fake"}}, - _source=['id', 'name', 'updated_at'], - all_projects=False, type=None) - - def test_search_regexp_slashes_in_query_string(self): - """Escape slashes in querystrings so not to be treated as regexp""" - self._test_search(["this/has/some/slashes"], - query={"query_string": {"query": r"this\/has\/some\/slashes"}}, - _source=['id', 'name', 'updated_at'], - all_projects=False, type=None) - - def test_search_regexp_slashes_in_query(self): - """Don't escape slashes in DSL queries""" - self._test_search(['--json', - '{"term": {"name": "this/has/some/slashes"}}'], - query={"term": {"name": "this/has/some/slashes"}}, - _source=['id', 'name', 'updated_at'], - all_projects=False, type=None) - - def test_search_query_dsl(self): - self._test_search(['--json', - '{"term": {"status": "active"}}'], - query={'term': {'status': 'active'}}, - _source=['id', 'name', 'updated_at'], - all_projects=False, type=None) - - def test_search_query_dsl_no_json_flag(self): - self.assertRaises( - SystemExit, self._test_search, - ['{"term": {"status": "active"}}'], - query={'term': {'status': 'active'}}, - _source=['id', 'name', 'updated_at'], - all_projects=False, type=None) - - def test_list_all_projects(self): - self._test_search(["name: fake", "--all-projects"], - query={"query_string": {"query": "name: fake"}}, - _source=['id', 'name', 'updated_at'], - all_projects=True, type=None) - - def test_list_source(self): - self._test_search(["name: fake", "--source"], - query={"query_string": {"query": "name: fake"}}, - all_projects=False, source="all_resources", - type=None) - - def test_list_optional_source(self): - self._test_search(["name: fake", "--source", "f1,f2"], - query={"query_string": {"query": "name: fake"}}, - all_projects=False, source=["id", "f1", "f2"], - type=None) diff --git a/searchlightclient/tests/unit/v1/__init__.py b/searchlightclient/tests/unit/v1/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/searchlightclient/tests/unit/v1/test_facets.py b/searchlightclient/tests/unit/v1/test_facets.py deleted file mode 100644 index f0960cf..0000000 --- a/searchlightclient/tests/unit/v1/test_facets.py +++ /dev/null @@ -1,44 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from searchlightclient.v1 import facets - -import testtools -from unittest import mock - - -class FacetsManagerTest(testtools.TestCase): - - def setUp(self): - super(FacetsManagerTest, self).setUp() - self.manager = facets.FacetsManager(None) - self.manager.client = mock.Mock() - - def test_list_all_projects(self): - self.manager.list(all_projects=True) - self.manager.client.get.assert_called_once_with( - '/v1/search/facets?all_projects=True') - - def test_list_by_type(self): - self.manager.list(type='fake_type') - self.manager.client.get.assert_called_once_with( - '/v1/search/facets?type=fake_type') - - def test_list_by_index(self): - self.manager.list(index='fake_index') - self.manager.client.get.assert_called_once_with( - '/v1/search/facets?index=fake_index') - - def test_list_by_limit_terms(self): - self.manager.list(limit_terms='10') - self.manager.client.get.assert_called_once_with( - '/v1/search/facets?limit_terms=10') diff --git a/searchlightclient/tests/unit/v1/test_resource_types.py b/searchlightclient/tests/unit/v1/test_resource_types.py deleted file mode 100644 index 9ce4af1..0000000 --- a/searchlightclient/tests/unit/v1/test_resource_types.py +++ /dev/null @@ -1,25 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from searchlightclient.v1 import resource_types - -import testtools -from unittest import mock - - -class ResourceTypeManagerTest(testtools.TestCase): - - def test_list(self): - manager = resource_types.ResourceTypeManager(None) - manager._list = mock.Mock() - manager.list() - manager._list.assert_called_once_with('/v1/search/plugins', 'plugins') diff --git a/searchlightclient/tests/unit/v1/test_search.py b/searchlightclient/tests/unit/v1/test_search.py deleted file mode 100644 index e81c0a2..0000000 --- a/searchlightclient/tests/unit/v1/test_search.py +++ /dev/null @@ -1,73 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from searchlightclient.v1 import search - -import testtools -from unittest import mock - - -class SearchManagerTest(testtools.TestCase): - - def setUp(self): - super(SearchManagerTest, self).setUp() - self.manager = search.SearchManager(None) - self.manager._post = mock.Mock() - - def test_search_with_query(self): - query_string = { - 'query_string': { - 'query': 'database' - } - } - self.manager.search(query=query_string) - self.manager._post.assert_called_once_with( - '/v1/search', {'query': query_string}) - - def test_search_with_type(self): - self.manager.search(type='fake_type') - self.manager._post.assert_called_once_with( - '/v1/search', {'type': 'fake_type'}) - - def test_search_with_offset(self): - self.manager.search(offset='fake_offset') - self.manager._post.assert_called_once_with( - '/v1/search', {'offset': 'fake_offset'}) - - def test_search_with_limit(self): - self.manager.search(limit=10) - self.manager._post.assert_called_once_with( - '/v1/search', {'limit': 10}) - - def test_search_with_sort(self): - self.manager.search(sort='asc') - self.manager._post.assert_called_once_with( - '/v1/search', {'sort': 'asc'}) - - def test_search_with_source(self): - self.manager.search(_source=['fake_source']) - self.manager._post.assert_called_once_with( - '/v1/search', {'_source': ['fake_source']}) - - def test_search_with_highlight(self): - self.manager.search(highlight='fake_highlight') - self.manager._post.assert_called_once_with( - '/v1/search', {'highlight': 'fake_highlight'}) - - def test_search_with_all_projects(self): - self.manager.search(all_projects=True) - self.manager._post.assert_called_once_with( - '/v1/search', {'all_projects': True}) - - def test_search_with_invalid_option(self): - self.manager.search(invalid='fake') - self.manager._post.assert_called_once_with('/v1/search', {}) diff --git a/searchlightclient/v1/__init__.py b/searchlightclient/v1/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/searchlightclient/v1/client.py b/searchlightclient/v1/client.py deleted file mode 100644 index 4e01c08..0000000 --- a/searchlightclient/v1/client.py +++ /dev/null @@ -1,42 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from searchlightclient import client -from searchlightclient.v1 import facets -from searchlightclient.v1 import resource_types -from searchlightclient.v1 import search - - -class Client(object): - """Client for the Searchlight v1 API. - - :param session: a keystoneauth/keystoneclient session object - :type session: keystoneclient.session.Session - :param str service_type: The default service_type for URL discovery - :param str interface: The default interface for URL discovery - (Default: public) - :param str region_name: The default region_name for URL discovery - :param str endpoint_override: Always use this endpoint URL for requests - for this ceiloclient - :param auth: An auth plugin to use instead of the session one - :type auth: keystoneclient.auth.base.BaseAuthPlugin - :param str user_agent: The User-Agent string to set - (Default is python-searchlightclient) - """ - - def __init__(self, *args, **kwargs): - """Initialize a new client for the Searchlight v1 API.""" - self.http_client = client._construct_http_client(*args, **kwargs) - self.resource_types = resource_types.ResourceTypeManager( - self.http_client) - self.facets = facets.FacetsManager(self.http_client) - self.search = search.SearchManager(self.http_client) diff --git a/searchlightclient/v1/facets.py b/searchlightclient/v1/facets.py deleted file mode 100644 index c297eb4..0000000 --- a/searchlightclient/v1/facets.py +++ /dev/null @@ -1,53 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from urllib import parse - -from searchlightclient.common import base - - -class Facets(base.Resource): - def __repr__(self): - return "" % self._info - - def list(self, **kwargs): - return self.manager.list(self, **kwargs) - - -class FacetsManager(base.BaseManager): - resource_class = Facets - - def list(self, **kwargs): - """Get a list of facets. - :param index: Index name to query. - :param all_projects: By default, facet terms are limited to the - currently scoped project. Administrators are - able to request facet terms for all projects - by specify all_projects=True. - :param limit_terms: Limit the number of options returned for fields - that support facet terms. - :param type: Request facets for a particular type by adding a type - query parameter. - :rtype: dict of {resource_type: {'facets': [:class:`Facets`], - 'doc_count':, :class:int}} - """ - params = {} - if kwargs.get('index'): - params['index'] = kwargs['index'] - if kwargs.get('type'): - params['type'] = kwargs['type'] - if kwargs.get('limit_terms'): - params['limit_terms'] = kwargs['limit_terms'] - if kwargs.get('all_projects') is not None: - params['all_projects'] = kwargs['all_projects'] - url = '/v1/search/facets?%s' % parse.urlencode(params, True) - return self.client.get(url).json() diff --git a/searchlightclient/v1/resource_types.py b/searchlightclient/v1/resource_types.py deleted file mode 100644 index 1df64c2..0000000 --- a/searchlightclient/v1/resource_types.py +++ /dev/null @@ -1,31 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from searchlightclient.common import base - - -class ResourceType(base.Resource): - def __repr__(self): - return "" % self._info - - def list(self, **kwargs): - return self.manager.list(self, **kwargs) - - -class ResourceTypeManager(base.BaseManager): - resource_class = ResourceType - - def list(self, **kwargs): - """Get a list of plugins. - :rtype: list of :class:`ResourceType` - """ - return self._list('/v1/search/plugins', 'plugins') diff --git a/searchlightclient/v1/search.py b/searchlightclient/v1/search.py deleted file mode 100644 index 052a0fb..0000000 --- a/searchlightclient/v1/search.py +++ /dev/null @@ -1,58 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from searchlightclient.common import base - - -class Search(base.Resource): - def __repr__(self): - return "" % self._info - - def search(self, **kwargs): - return self.manager.search(self, **kwargs) - - -class SearchManager(base.BaseManager): - resource_class = Search - - def search(self, **kwargs): - """Executes a search query against searchlight and returns the 'hits' - from the response. Currently accepted parameters are (all optional): - - :param query: see Elasticsearch DSL or Searchlight documentation; - defaults to match everything - :param type: one or more types to search. Uniquely identifies resource - types. Example: OS::Glance::Image - :param offset: skip over this many results - :param limit: return this many results - :param sort: sort by one or more fields - :param _source: restrict the fields returned for each document - :param highlight: add an Elasticsearch highlight clause - :param all_projects: by default searches are restricted to the - current project unless all_projects is set - :param simplified: return only _source data - """ - search_params = {} - for k, v in kwargs.items(): - if k in ('query', 'type', 'offset', - 'limit', 'sort', '_source', 'highlight', 'all_projects'): - search_params[k] = v - resources = self._post('/v1/search', search_params) - - # NOTE: This could be done at the server side to reduce data - # transfer, since the data have been wrapped several times - # before transfer, and the data overhead is pretty small comparing - # to the data payload('_source'), it is done here for simplicity. - if 'simplified' in kwargs and kwargs['simplified']: - resources = [h['_source'] for h in resources.hits['hits']] - - return resources diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 20149da..0000000 --- a/setup.cfg +++ /dev/null @@ -1,35 +0,0 @@ -[metadata] -name = python-searchlightclient -summary = OpenStack Indexing and Search API Client Library -description-file = - README.rst -author = OpenStack -author-email = openstack-discuss@lists.openstack.org -home-page = https://docs.openstack.org/python-searchlightclient/latest -python-requires = >=3.6 -classifier = - Environment :: OpenStack - Intended Audience :: Information Technology - Intended Audience :: System Administrators - License :: OSI Approved :: Apache Software License - Operating System :: POSIX :: Linux - Programming Language :: Python - Programming Language :: Python :: Implementation :: CPython - Programming Language :: Python :: 3 :: Only - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.6 - Programming Language :: Python :: 3.7 - Programming Language :: Python :: 3.8 - -[files] -packages = - searchlightclient - -[entry_points] -openstack.cli.extension = - search = searchlightclient.osc.plugin - -openstack.search.v1 = - search_resource_type_list = searchlightclient.osc.v1.resource_type:ListResourceType - search_facet_list = searchlightclient.osc.v1.facet:ListFacet - search_query = searchlightclient.osc.v1.search:SearchResource diff --git a/setup.py b/setup.py deleted file mode 100644 index cd35c3c..0000000 --- a/setup.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (c) 2013 Hewlett-Packard Development Company, L.P. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import setuptools - -setuptools.setup( - setup_requires=['pbr>=2.0.0'], - pbr=True) diff --git a/test-requirements.txt b/test-requirements.txt deleted file mode 100644 index 6136bd0..0000000 --- a/test-requirements.txt +++ /dev/null @@ -1,10 +0,0 @@ -# The order of packages is significant, because pip processes them in the order -# of appearance. Changing the order has an impact on the overall integration -# process, which may cause wedges in the gate later. - -# Hacking already pins down pep8, pyflakes and flake8 -hacking>=3.0.1,<3.1.0 # Apache-2.0 -coverage!=4.4,>=4.0 # Apache-2.0 -fixtures>=3.0.0 # Apache-2.0/BSD -stestr>=2.0.0 # Apache-2.0 -testtools>=2.2.0 # MIT diff --git a/tools/install_venv.py b/tools/install_venv.py deleted file mode 100644 index b5b5309..0000000 --- a/tools/install_venv.py +++ /dev/null @@ -1,75 +0,0 @@ -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# All Rights Reserved. -# -# Copyright 2010 OpenStack Foundation -# Copyright 2013 IBM Corp. -# Copyright (c) 2013 Hewlett-Packard Development Company, L.P. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import ConfigParser -import os -import sys - -import install_venv_common as install_venv # flake8: noqa - - -def print_help(project, venv, root): - help = """ - %(project)s development environment setup is complete. - - %(project)s development uses virtualenv to track and manage Python - dependencies while in development and testing. - - To activate the %(project)s virtualenv for the extent of your current - shell session you can run: - - $ source %(venv)s/bin/activate - - Or, if you prefer, you can run commands in the virtualenv on a case by - case basis by running: - - $ %(root)s/tools/with_venv.sh - """ - print(help % dict(project=project, venv=venv, root=root)) - - -def main(argv): - root = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) - - if os.environ.get('tools_path'): - root = os.environ['tools_path'] - venv = os.path.join(root, '.venv') - if os.environ.get('venv'): - venv = os.environ['venv'] - - pip_requires = os.path.join(root, 'requirements.txt') - test_requires = os.path.join(root, 'test-requirements.txt') - py_version = "python%s.%s" % (sys.version_info[0], sys.version_info[1]) - setup_cfg = ConfigParser.ConfigParser() - setup_cfg.read('setup.cfg') - project = setup_cfg.get('metadata', 'name') - - install = install_venv.InstallVenv( - root, venv, pip_requires, test_requires, py_version, project) - options = install.parse_args(argv) - install.check_python_version() - install.check_dependencies() - install.create_virtualenv(no_site_packages=options.no_site_packages) - install.install_dependencies() - print_help(project, venv, root) - - -if __name__ == '__main__': - main(sys.argv) diff --git a/tools/install_venv_common.py b/tools/install_venv_common.py deleted file mode 100644 index d73649c..0000000 --- a/tools/install_venv_common.py +++ /dev/null @@ -1,170 +0,0 @@ -# Copyright 2013 OpenStack Foundation -# Copyright 2013 IBM Corp. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -"""Provides methods needed by installation script for OpenStack development -virtual environments. - -Since this script is used to bootstrap a virtualenv from the system's Python -environment, it should be kept strictly compatible with Python 2.6. - -Synced in from openstack-common -""" - -import optparse -import os -import subprocess -import sys - - -class InstallVenv(object): - - def __init__(self, root, venv, requirements, - test_requirements, py_version, - project): - self.root = root - self.venv = venv - self.requirements = requirements - self.test_requirements = test_requirements - self.py_version = py_version - self.project = project - - def die(self, message, *args): - print(message % args, file=sys.stderr) - sys.exit(1) - - def check_python_version(self): - if sys.version_info < (2, 6): - self.die("Need Python Version >= 2.6") - - def run_command_with_code(self, cmd, redirect_output=True, - check_exit_code=True): - """Runs a command in an out-of-process shell. - - Returns the output of that command. Working directory is self.root. - """ - if redirect_output: - stdout = subprocess.PIPE - else: - stdout = None - - proc = subprocess.Popen(cmd, cwd=self.root, stdout=stdout) - output = proc.communicate()[0] - if check_exit_code and proc.returncode != 0: - self.die('Command "%s" failed.\n%s', ' '.join(cmd), output) - return (output, proc.returncode) - - def run_command(self, cmd, redirect_output=True, check_exit_code=True): - return self.run_command_with_code(cmd, redirect_output, - check_exit_code)[0] - - def get_distro(self): - if (os.path.exists('/etc/fedora-release') or - os.path.exists('/etc/redhat-release')): - return Fedora( - self.root, self.venv, self.requirements, - self.test_requirements, self.py_version, self.project) - else: - return Distro( - self.root, self.venv, self.requirements, - self.test_requirements, self.py_version, self.project) - - def check_dependencies(self): - self.get_distro().install_virtualenv() - - def create_virtualenv(self, no_site_packages=True): - """Creates the virtual environment and installs PIP. - - Creates the virtual environment and installs PIP only into the - virtual environment. - """ - if not os.path.isdir(self.venv): - print('Creating venv...', end=' ') - if no_site_packages: - self.run_command(['virtualenv', '-q', '--no-site-packages', - self.venv]) - else: - self.run_command(['virtualenv', '-q', self.venv]) - print('done.') - else: - print("venv already exists...") - pass - - def pip_install(self, *args): - self.run_command(['tools/with_venv.sh', - 'pip', 'install', '--upgrade'] + list(args), - redirect_output=False) - - def install_dependencies(self): - print('Installing dependencies with pip (this can take a while)...') - - # First things first, make sure our venv has the latest pip and - # setuptools and pbr - self.pip_install('pip>=1.4') - self.pip_install('setuptools') - self.pip_install('pbr') - - self.pip_install('-r', self.requirements, '-r', self.test_requirements) - - def parse_args(self, argv): - """Parses command-line arguments.""" - parser = optparse.OptionParser() - parser.add_option('-n', '--no-site-packages', - action='store_true', - help="Do not inherit packages from global Python " - "install") - return parser.parse_args(argv[1:])[0] - - -class Distro(InstallVenv): - - def check_cmd(self, cmd): - return bool(self.run_command(['which', cmd], - check_exit_code=False).strip()) - - def install_virtualenv(self): - if self.check_cmd('virtualenv'): - return - - if self.check_cmd('easy_install'): - print('Installing virtualenv via easy_install...', end=' ') - if self.run_command(['easy_install', 'virtualenv']): - print('Succeeded') - return - else: - print('Failed') - - self.die('ERROR: virtualenv not found.\n\n%s development' - ' requires virtualenv, please install it using your' - ' favorite package management tool' % self.project) - - -class Fedora(Distro): - """This covers all Fedora-based distributions. - - Includes: Fedora, RHEL, CentOS, Scientific Linux - """ - - def check_pkg(self, pkg): - return self.run_command_with_code(['rpm', '-q', pkg], - check_exit_code=False)[1] == 0 - - def install_virtualenv(self): - if self.check_cmd('virtualenv'): - return - - if not self.check_pkg('python-virtualenv'): - self.die("Please install 'python-virtualenv'.") - - super(Fedora, self).install_virtualenv() diff --git a/tools/with_venv.sh b/tools/with_venv.sh deleted file mode 100755 index e6e44f5..0000000 --- a/tools/with_venv.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -command -v tox > /dev/null 2>&1 -if [ $? -ne 0 ]; then - echo 'This script requires "tox" to run.' - echo 'You can install it with "pip install tox".' - exit 1; -fi - -tox -evenv -- $@ diff --git a/tox.ini b/tox.ini deleted file mode 100644 index f7dac64..0000000 --- a/tox.ini +++ /dev/null @@ -1,77 +0,0 @@ -[tox] -envlist = py37,pep8 -minversion = 3.1.1 -skipsdist = True -ignore_basepython_conflict = True - -[testenv] -basepython = python3 -usedevelop = True -install_command = pip install {opts} {packages} -deps = - -c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} - -r{toxinidir}/requirements.txt - -r{toxinidir}/test-requirements.txt -commands = - find . -type f -name "*.pyc" -delete - stestr run --slowest {posargs} -whitelist_externals = find - -[testenv:pep8] -sitepackages = False -commands = flake8 - -[testenv:venv] -commands = {posargs} - -[testenv:functional] -setenv = - OS_TEST_PATH = ./searchlightclient/tests/functional -passenv = OS_* - -[testenv:cover] -setenv = - VIRTUAL_ENV={envdir} - PYTHON=coverage run --source searchlightclient --parallel-mode -commands = - stestr run {posargs} - coverage combine - coverage html -d cover - coverage xml -o cover/coverage.xml - coverage report - -[testenv:docs] -whitelist_externals = - rm -deps = -r{toxinidir}/doc/requirements.txt -commands = - rm -rf doc/build - sphinx-build --keep-going -b html -d doc/build/doctrees doc/source doc/build/html - -[testenv:pdf-docs] -deps = -r{toxinidir}/doc/requirements.txt -envdir = {toxworkdir}/docs -whitelist_externals = - make -commands = - sphinx-build -W -b latex doc/source doc/build/pdf - make -C doc/build/pdf - -[flake8] -# E128 continuation line under-indented for visual indent -# E265 block comment should start with '# ' -# H405: multi line docstring summary not separated with an empty line -# W504 line break after binary operator -ignore = E128,E265,H405,W504 -show-source = True -exclude=.venv,.git,.tox,dist,*lib/python*,*egg,build -max-complexity=20 - -[hacking] -import_exceptions = searchlightclient.openstack.common._i18n - -[testenv:lower-constraints] -deps = - -c{toxinidir}/lower-constraints.txt - -r{toxinidir}/test-requirements.txt - -r{toxinidir}/requirements.txt