diff --git a/.coveragerc b/.coveragerc deleted file mode 100644 index 16f936f..0000000 --- a/.coveragerc +++ /dev/null @@ -1,6 +0,0 @@ -[run] -branch = True -source = pankoclient - -[report] -ignore_errors = True diff --git a/.testr.conf b/.testr.conf deleted file mode 100644 index 6d83b3c..0000000 --- a/.testr.conf +++ /dev/null @@ -1,7 +0,0 @@ -[DEFAULT] -test_command=OS_STDOUT_CAPTURE=${OS_STDOUT_CAPTURE:-1} \ - OS_STDERR_CAPTURE=${OS_STDERR_CAPTURE:-1} \ - OS_TEST_TIMEOUT=${OS_TEST_TIMEOUT:-60} \ - ${PYTHON:-python} -m subunit.run discover -t ./ . $LISTOPT $IDOPTION -test_id_option=--load-list $IDFILE -test_list_option=--list diff --git a/.zuul.yaml b/.zuul.yaml deleted file mode 100644 index f521eeb..0000000 --- a/.zuul.yaml +++ /dev/null @@ -1,5 +0,0 @@ -- project: - templates: - - openstack-python3-xena-jobs - - publish-openstack-docs-pti - - openstackclient-plugin-jobs diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst deleted file mode 100644 index 2191f58..0000000 --- a/CONTRIBUTING.rst +++ /dev/null @@ -1,17 +0,0 @@ -If you would like to contribute to the development of OpenStack, you must -follow the steps in this page: - - http://docs.openstack.org/infra/manual/developers.html - -If you already have a good understanding of how the system works and your -OpenStack accounts are set up, you can skip to the development workflow -section of this documentation to learn how changes to OpenStack should be -submitted for review via the Gerrit tool: - - http://docs.openstack.org/infra/manual/developers.html#development-workflow - -Pull requests submitted through GitHub will be ignored. - -Bugs should be filed on Launchpad, not GitHub: - - https://bugs.launchpad.net/python-pankoclient diff --git a/HACKING.rst b/HACKING.rst deleted file mode 100644 index 1403b81..0000000 --- a/HACKING.rst +++ /dev/null @@ -1,4 +0,0 @@ -python-pankoclient Style Commandments -===================================== - -Read the OpenStack Style Commandments https://docs.openstack.org/hacking/latest/ diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 68c771a..0000000 --- a/LICENSE +++ /dev/null @@ -1,176 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index c978a52..0000000 --- a/MANIFEST.in +++ /dev/null @@ -1,6 +0,0 @@ -include AUTHORS -include ChangeLog -exclude .gitignore -exclude .gitreview - -global-exclude *.pyc diff --git a/README.rst b/README.rst index 39a5956..4adf472 100644 --- a/README.rst +++ b/README.rst @@ -1,26 +1,7 @@ -========================================== -Python bindings to the OpenStack Panko API -========================================== +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". -.. image:: https://img.shields.io/pypi/v/pankoclient.svg - :target: https://pypi.org/project/pankoclient/ - :alt: Latest Version - -This is a client library for Panko built on the Panko API. It provides a -Python API (the ``pankoclient`` module) and a OSC (the openstackclient CLI) -command-line tool (``openstack event``). - -* Free software: Apache license -* `PyPi`_ - package installation -* `Online Documentation`_ -* `Launchpad project`_ - release management -* `Blueprints`_ - feature specifications -* `Bugs`_ - issue tracking -* `Source`_ - -.. _PyPi: https://pypi.org/project/pankoclient -.. _Online Documentation: https://docs.openstack.org/python-pankoclient/latest/ -.. _Launchpad project: https://launchpad.net/python-pankoclient -.. _Blueprints: https://blueprints.launchpad.net/python-pankoclient -.. _Bugs: https://bugs.launchpad.net/python-pankoclient -.. _Source: https://opendev.org/openstack/python-pankoclient +For any further questions, please email +openstack-discuss@lists.openstack.org or join #openstack-telemetry on OFTC. diff --git a/bindep.txt b/bindep.txt deleted file mode 100644 index 0558f69..0000000 --- a/bindep.txt +++ /dev/null @@ -1 +0,0 @@ -python37 [platform:rpm py37] diff --git a/doc/requirements.txt b/doc/requirements.txt deleted file mode 100644 index eccc937..0000000 --- a/doc/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -sphinx>=2.0.0,!=2.1.0 # BSD -openstackdocstheme>=2.2.1 # Apache-2.0 diff --git a/doc/source/api.rst b/doc/source/api.rst deleted file mode 100644 index 1e83ed9..0000000 --- a/doc/source/api.rst +++ /dev/null @@ -1,27 +0,0 @@ -The :mod:`pankoclient` Python API -================================= - -.. module:: pankoclient - :synopsis: A client for the Panko API. - -.. currentmodule:: pankoclient - -Usage ------ - -To use pankoclient in a project:: - - >>> from pankoclient.v2 import client - >>> panko = client.Client(...) - >>> panko.event.list() - -Reference ---------- - -For more information, see the reference: - -.. toctree:: - :maxdepth: 2 - - ref/v2/index - diff --git a/doc/source/conf.py b/doc/source/conf.py deleted file mode 100644 index 06c8f70..0000000 --- a/doc/source/conf.py +++ /dev/null @@ -1,122 +0,0 @@ -# -*- coding: utf-8 -*- -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os -import sys - -BASE_DIR = os.path.dirname(os.path.abspath(__file__)) -ROOT = os.path.abspath(os.path.join(BASE_DIR, "..", "..")) - -sys.path.insert(0, ROOT) -sys.path.insert(0, BASE_DIR) - - -def gen_ref(ver, title, names): - refdir = os.path.join(BASE_DIR, "ref") - pkg = "pankoclient" - if ver: - pkg = "%s.%s" % (pkg, ver) - refdir = os.path.join(refdir, ver) - if not os.path.exists(refdir): - os.makedirs(refdir) - idxpath = os.path.join(refdir, "index.rst") - with open(idxpath, "w") as idx: - idx.write(("%(title)s\n" - "%(signs)s\n" - "\n" - ".. toctree::\n" - " :maxdepth: 1\n" - "\n") % {"title": title, "signs": "=" * len(title)}) - for name in names: - idx.write(" %s\n" % name) - rstpath = os.path.join(refdir, "%s.rst" % name) - with open(rstpath, "w") as rst: - rst.write(("%(title)s\n" - "%(signs)s\n" - "\n" - ".. automodule:: %(pkg)s.%(name)s\n" - " :members:\n" - " :undoc-members:\n" - " :show-inheritance:\n" - " :noindex:\n") - % {"title": " ".join([n.capitalize() - for n in name.split("_")]), - "signs": "=" * len(name), - "pkg": pkg, "name": name}) - -gen_ref("v2", "Version 2 API", ["client"]) - -# -- 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 = [ - 'openstackdocstheme', - 'sphinx.ext.autodoc', - #'sphinx.ext.intersphinx', -] - -# autodoc generation is a bit aggressive and a nuisance when doing heavy -# text edit cycles. -# execute "export SPHINX_DEBUG=1" in your terminal to disable - -# The suffix of source filenames. -source_suffix = '.rst' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -project = u'pankoclient' -copyright = u'2015, OpenStack Foundation' - -# 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 - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'native' - -# -- Options for HTML output -------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. Major themes that come with -# Sphinx are currently 'default' and 'sphinxdoc'. -# html_theme_path = ["."] -# html_theme = '_theme' -# html_static_path = ['static'] -html_theme = 'openstackdocs' - -# openstackdocstheme options -openstackdocs_repo_name = 'openstack/python-pankoclient' -openstackdocs_bug_project = 'python-pankoclient' -openstackdocs_bug_tag = '' - -# Output file base name for HTML help builder. -htmlhelp_basename = '%sdoc' % project - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, author, documentclass -# [howto/manual]). -latex_documents = [ - ('index', - '%s.tex' % project, - u'%s Documentation' % project, - u'OpenStack Foundation', 'manual'), -] - -# Example configuration for intersphinx: refer to the Python standard library. -#intersphinx_mapping = {'http://docs.python.org/': None} diff --git a/doc/source/contributing.rst b/doc/source/contributing.rst deleted file mode 100644 index 1728a61..0000000 --- a/doc/source/contributing.rst +++ /dev/null @@ -1,4 +0,0 @@ -============ -Contributing -============ -.. include:: ../../CONTRIBUTING.rst diff --git a/doc/source/index.rst b/doc/source/index.rst deleted file mode 100644 index e205a76..0000000 --- a/doc/source/index.rst +++ /dev/null @@ -1,46 +0,0 @@ -.. pankoclient documentation master file, created by - sphinx-quickstart on Tue Jul 9 22:26:36 2013. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. - -Python bindings to the Panko API -================================ - -This is a client for Panko API. There's :doc:`a Python API -` (the :mod:`pankoclient` module), and a set of event related commands -which are integrated with the OSC CLI tool. Each implements the entire Panko -API. - -.. warning:: - - This is a new client to interact with Panko API. There may be differences - in functionality, syntax, and command line output when compared with the - event functionality provided by ceilometerclient. - - -.. seealso:: - - You may want to read the `Panko Developer Guide`__ -- the overview, at - least -- to get an idea of the concepts. By understanding the concepts - this library should make more sense. - - __ https://docs.openstack.org/panko/latest/ - - -Contents: - -.. toctree:: - :maxdepth: 2 - - installation - osc_integrated_commands - api - contributing - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` - diff --git a/doc/source/installation.rst b/doc/source/installation.rst deleted file mode 100644 index 52cab63..0000000 --- a/doc/source/installation.rst +++ /dev/null @@ -1,8 +0,0 @@ -============ -Installation -============ - -At the command line:: - - $ pip install pankoclient - diff --git a/doc/source/osc_integrated_commands.rst b/doc/source/osc_integrated_commands.rst deleted file mode 100644 index 1ee690d..0000000 --- a/doc/source/osc_integrated_commands.rst +++ /dev/null @@ -1,42 +0,0 @@ -The OSC CLI plugins -=================== - -This program provides a set of event related commands which are performed by -OSC `openstack program`_. The `OpenStackClient`_ project provide a plugin -mechanism which supports loading other external projects' commands from -clients library entry points. - -The event related commands begin with `openstack event` in `openstack` -command tool. - -To configure the environment variables, see `openstack program`_. - -.. _OpenStackClient: https://docs.openstack.org/python-openstackclient/latest/ -.. _openstack program: https://docs.openstack.org/python-openstackclient/latest/cli/man/openstack.html - -Examples --------- - -List the capabilities of event interfaces:: - - openstack event capabilities list - -List the events:: - - openstack event list - -Show an event:: - - openstack event show - -List event types:: - - openstack event type list - -List trait values of a specified event type and a trait name:: - - openstack event traits list - -List traits definitions for a specified event type:: - - openstack event trait description diff --git a/pankoclient/__init__.py b/pankoclient/__init__.py deleted file mode 100644 index 57d2a5a..0000000 --- a/pankoclient/__init__.py +++ /dev/null @@ -1,21 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright 2017 Huawei, Inc. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import pbr.version - - -__version__ = pbr.version.VersionInfo( - 'pankoclient').version_string() diff --git a/pankoclient/client.py b/pankoclient/client.py deleted file mode 100644 index 166988e..0000000 --- a/pankoclient/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 keystoneauth1 import adapter -from oslo_utils import importutils -from osprofiler import web - -from pankoclient import exceptions - - -def Client(version, *args, **kwargs): - module = 'pankoclient.v%s.client' % version - module = importutils.import_module(module) - client_class = getattr(module, 'Client') - return client_class(*args, **kwargs) - - -class SessionClient(adapter.Adapter): - def request(self, url, method, **kwargs): - kwargs.setdefault('headers', kwargs.get('headers', {})) - # NOTE(sileht): The standard call raises errors from - # keystoneauth, where we need to raise the pankoclient errors. - raise_exc = kwargs.pop('raise_exc', True) - kwargs['headers'].update(web.get_trace_id_headers()) - resp = super(SessionClient, self).request(url, - method, - raise_exc=False, - **kwargs) - - if raise_exc and resp.status_code >= 400: - raise exceptions.from_response(resp, url, method) - return resp, resp.json() diff --git a/pankoclient/common/__init__.py b/pankoclient/common/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/pankoclient/common/base.py b/pankoclient/common/base.py deleted file mode 100644 index 6cc20f2..0000000 --- a/pankoclient/common/base.py +++ /dev/null @@ -1,374 +0,0 @@ -# Copyright 2017 Huawei, Inc. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -""" -Base utilities to build API operation managers and objects on top of. -""" - -import abc -import copy - -from requests import Response -import six - -from pankoclient import exceptions - - -def getid(obj): - """Get obj's uuid or object itself if no uuid - - Abstracts the common pattern of allowing both an object or - an object's ID (UUID) as a parameter when dealing with relationships. - """ - try: - return obj.uuid - except AttributeError: - return obj - - -class Manager(object): - """Interacts with type of API - - Managers interact with a particular type of API (instances, types, etc.) - and provide CRUD operations for them. - """ - resource_class = None - - def __init__(self, api): - self.api = api - - def _list(self, url, response_key=None, obj_class=None, - data=None, headers=None): - - if headers is None: - headers = {} - resp, body = self.api.get(url, headers=headers) - - if obj_class is None: - obj_class = self.resource_class - - if response_key: - if response_key not in body: - body[response_key] = [] - data = body[response_key] - else: - data = body - if all([isinstance(res, six.string_types) for res in data]): - items = data - else: - items = [obj_class(self, res, loaded=True) for res in data if res] - - return ListWithMeta(items, resp) - - def _delete(self, url, headers=None): - if headers is None: - headers = {} - resp, body = self.api.delete(url, headers=headers) - - return self.convert_into_with_meta(body, resp) - - def _update(self, url, data, response_key=None, return_raw=False, - headers=None): - if headers is None: - headers = {} - resp, body = self.api.patch(url, data=data, headers=headers) - if return_raw: - if response_key: - body = body[response_key] - return self.convert_into_with_meta(body, resp) - # PATCH requests may not return a body - if body: - if response_key: - return self.resource_class(self, body[response_key], resp=resp) - return self.resource_class(self, body, resp=resp) - else: - return StrWithMeta(body, resp) - - def _update_all(self, url, data, response_key=None, return_raw=False, - headers=None): - if headers is None: - headers = {} - resp, body = self.api.put(url, data=data, headers=headers) - if return_raw: - if response_key: - body = body[response_key] - return self.convert_into_with_meta(body, resp) - # PUT requests may not return a body - if body: - if response_key: - return self.resource_class(self, body[response_key], resp=resp) - return self.resource_class(self, body, resp=resp) - else: - return StrWithMeta(body, resp) - - def _create(self, url, data=None, response_key=None, return_raw=False, - headers=None): - if headers is None: - headers = {} - if data: - resp, body = self.api.post(url, data=data, headers=headers) - else: - resp, body = self.api.post(url, headers=headers) - if return_raw: - if response_key: - body = body[response_key] - return self.convert_into_with_meta(body, resp) - - if response_key: - return self.resource_class(self, body[response_key], resp=resp) - return self.resource_class(self, body, resp=resp) - - def _get(self, url, response_key=None, return_raw=False, headers=None): - if headers is None: - headers = {} - resp, body = self.api.get(url, headers=headers) - if return_raw: - if response_key: - body = body[response_key] - return self.convert_into_with_meta(body, resp) - - if response_key: - return self.resource_class(self, body[response_key], loaded=True, - resp=resp) - return self.resource_class(self, body, loaded=True, resp=resp) - - def convert_into_with_meta(self, item, resp): - if isinstance(item, six.string_types): - if six.PY2 and isinstance(item, six.text_type): - return UnicodeWithMeta(item, resp) - else: - return StrWithMeta(item, resp) - elif isinstance(item, six.binary_type): - return BytesWithMeta(item, resp) - elif isinstance(item, list): - return ListWithMeta(item, resp) - elif isinstance(item, tuple): - return TupleWithMeta(item, resp) - elif item is None: - return TupleWithMeta((), resp) - else: - return DictWithMeta(item, resp) - - -@six.add_metaclass(abc.ABCMeta) -class ManagerWithFind(Manager): - """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 = len(matches) - - if num == 0: - msg = "No %s matching %s." % (self.resource_class.__name__, kwargs) - raise exceptions.NotFound(msg) - elif num > 1: - raise exceptions.NoUniqueMatch - else: - return self.get(matches[0].uuid) - - 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 RequestIdMixin(object): - """Wrapper class to expose x-openstack-request-id to the caller.""" - def request_ids_setup(self): - self.x_openstack_request_ids = [] - - @property - def request_ids(self): - return self.x_openstack_request_ids - - def append_request_ids(self, resp): - """Add request_ids as an attribute to the object - - :param resp: Response object or list of Response objects - """ - if isinstance(resp, list): - # Add list of request_ids if response is of type list. - for resp_obj in resp: - self._append_request_id(resp_obj) - elif resp is not None: - # Add request_ids if response contains single object. - self._append_request_id(resp) - - def _append_request_id(self, resp): - if isinstance(resp, Response): - # Extract 'X-Openstack-Request-Id' from headers if - # response is a Response object. - request_id = (resp.headers.get('Openstack-Request-Id') or - resp.headers.get('x-openstack-request-id') or - resp.headers.get('x-compute-request-id')) - else: - # If resp is of type string or None. - request_id = resp - if request_id not in self.x_openstack_request_ids: - self.x_openstack_request_ids.append(request_id) - - -class Resource(RequestIdMixin): - """Represents an instance of an object - - A resource represents a particular instance of an object (instance, type, - etc). This is pretty much just a bag for attributes. - - :param manager: BaseManager object - :param info: dictionary representing resource attributes - :param loaded: prevent lazy-loading if set to True - :param resp: Response or list of Response objects - """ - - def __init__(self, manager, info, loaded=False, resp=None): - self.manager = manager - self._info = info - self._add_details(info) - self._loaded = loaded - self.request_ids_setup() - self.append_request_ids(resp) - - def _add_details(self, info): - for (k, v) in six.iteritems(info): - try: - setattr(self, k, v) - self._info[k] = v - except AttributeError: - # In this case we already defined the attribute on the class - pass - - def __setstate__(self, d): - for k, v in d.items(): - setattr(self, k, v) - - def __getattr__(self, k): - if k not in self.__dict__: - # NOTE(RuiChen): 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 __repr__(self): - reprkeys = sorted(k for k in self.__dict__.keys() if k[0] != '_' and - k not in ('manager', 'x_openstack_request_ids')) - info = ", ".join("%s=%s" % (k, getattr(self, k)) for k in reprkeys) - return "<%s %s>" % (self.__class__.__name__, info) - - def get(self): - # 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.uuid) - if new: - self._add_details(new._info) - # The 'request_ids' attribute has been added, - # so store the request id to it instead of _info - self.append_request_ids(new.request_ids) - - def __eq__(self, other): - if not isinstance(other, self.__class__): - return False - return self._info == other._info - - def __ne__(self, other): - return not self.__eq__(other) - - def is_loaded(self): - return self._loaded - - def set_loaded(self, val): - self._loaded = val - - def to_dict(self): - return copy.deepcopy(self._info) - - -class ListWithMeta(list, RequestIdMixin): - def __init__(self, values, resp): - super(ListWithMeta, self).__init__(values) - self.request_ids_setup() - self.append_request_ids(resp) - - -class DictWithMeta(dict, RequestIdMixin): - def __init__(self, values, resp): - super(DictWithMeta, self).__init__(values) - self.request_ids_setup() - self.append_request_ids(resp) - - -class TupleWithMeta(tuple, RequestIdMixin): - def __new__(cls, values, resp): - return super(TupleWithMeta, cls).__new__(cls, values) - - def __init__(self, values, resp): - self.request_ids_setup() - self.append_request_ids(resp) - - -class StrWithMeta(str, RequestIdMixin): - def __new__(cls, value, resp): - return super(StrWithMeta, cls).__new__(cls, value) - - def __init__(self, values, resp): - self.request_ids_setup() - self.append_request_ids(resp) - - -class BytesWithMeta(six.binary_type, RequestIdMixin): - def __new__(cls, value, resp): - return super(BytesWithMeta, cls).__new__(cls, value) - - def __init__(self, values, resp): - self.request_ids_setup() - self.append_request_ids(resp) - - -if six.PY2: - class UnicodeWithMeta(six.text_type, RequestIdMixin): - def __new__(cls, value, resp): - return super(UnicodeWithMeta, cls).__new__(cls, value) - - def __init__(self, values, resp): - self.request_ids_setup() - self.append_request_ids(resp) diff --git a/pankoclient/common/i18n.py b/pankoclient/common/i18n.py deleted file mode 100644 index 83cb436..0000000 --- a/pankoclient/common/i18n.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright 2017 Huawei, Inc. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -import oslo_i18n - -_translators = oslo_i18n.TranslatorFactory(domain='pankoclient') - -# The primary translation function using the well-known name "_" -_ = _translators.primary diff --git a/pankoclient/exceptions.py b/pankoclient/exceptions.py deleted file mode 100644 index 2e59718..0000000 --- a/pankoclient/exceptions.py +++ /dev/null @@ -1,193 +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. - - -class ClientException(Exception): - """The base exception class for all exceptions this library raises.""" - message = 'Unknown Error' - http_status = 'N/A' - - def __init__(self, message=None, request_id=None, - url=None, method=None): - self.message = message or self.__class__.message - self.request_id = request_id - self.url = url - self.method = method - - # NOTE(jd) for backward compat - @property - def code(self): - return self.http_status - - def __str__(self): - formatted_string = "%s (HTTP %s)" % (self.message, self.http_status) - if self.request_id: - formatted_string += " (Request-ID: %s)" % self.request_id - - return formatted_string - - -class RetryAfterException(ClientException): - """The base exception for ClientExceptions that use Retry-After header.""" - def __init__(self, *args, **kwargs): - try: - self.retry_after = int(kwargs.pop('retry_after')) - except (KeyError, ValueError): - self.retry_after = 0 - - super(RetryAfterException, self).__init__(*args, **kwargs) - - -class MutipleMeaningException(object): - """An mixin for exception that can be enhanced by reading the details""" - - -class CommandError(Exception): - pass - - -class BadRequest(ClientException): - """HTTP 400 - Bad request: you sent some malformed data.""" - http_status = 400 - message = "Bad request" - - -class Unauthorized(ClientException): - """HTTP 401 - Unauthorized: bad credentials.""" - http_status = 401 - message = "Unauthorized" - - -class Forbidden(ClientException): - """HTTP 403 - Forbidden: - - your credentials don't give you access to this resource. - """ - http_status = 403 - message = "Forbidden" - - -class NotFound(ClientException): - """HTTP 404 - Not found""" - http_status = 404 - message = "Not found" - - -class MethodNotAllowed(ClientException): - """HTTP 405 - Method Not Allowed""" - http_status = 405 - message = "Method Not Allowed" - - -class NotAcceptable(ClientException): - """HTTP 406 - Not Acceptable""" - http_status = 406 - message = "Not Acceptable" - - -class Conflict(ClientException): - """HTTP 409 - Conflict""" - http_status = 409 - message = "Conflict" - - -class OverLimit(RetryAfterException): - """HTTP 413 - Over limit: - - you're over the API limits for this time period. - """ - http_status = 413 - message = "Over limit" - - -class RateLimit(RetryAfterException): - """HTTP 429 - Rate limit: - - you've sent too many requests for this time period. - """ - http_status = 429 - message = "Rate limit" - - -class NoUniqueMatch(Exception): - pass - - -class NotImplemented(ClientException): - """HTTP 501 - Not Implemented: - - the server does not support this operation. - """ - http_status = 501 - message = "Not Implemented" - - -_error_classes = [BadRequest, Unauthorized, Forbidden, NotFound, - MethodNotAllowed, NotAcceptable, Conflict, OverLimit, - RateLimit, NotImplemented] -_error_classes_enhanced = {} -_code_map = dict( - (c.http_status, (c, _error_classes_enhanced.get(c, []))) - for c in _error_classes) - - -def from_response(response, url, method=None): - """Return an instance of one of the ClientException on an requests response. - - Usage:: - resp = requests.request(...) - if resp.status_code != 200: - raise exception_from_response(resp) - """ - - if response.status_code: - cls, enhanced_classes = _code_map.get(response.status_code, - (ClientException, [])) - - req_id = response.headers.get("x-openstack-request-id") - content_type = response.headers.get("Content-Type", "").split(";")[0] - - kwargs = { - 'method': method, - 'url': url, - 'request_id': req_id, - } - - if "retry-after" in response.headers: - kwargs['retry_after'] = response.headers.get('retry-after') - if content_type == "application/json": - try: - body = response.json() - except ValueError: - pass - else: - error_message = body.get('error_message', {}) - try: - desc = error_message.get('faultstring') - except AttributeError: - desc = error_message - for enhanced_cls in enhanced_classes: - if enhanced_cls.match.match(desc): - cls = enhanced_cls - break - kwargs['message'] = desc - elif content_type.startswith("text/"): - kwargs['message'] = response.text - - if not kwargs.get('message'): - kwargs.pop('message', None) - - exception = cls(**kwargs) - if isinstance(exception, ClientException) and response.status_code: - exception.http_status = response.status_code - return exception diff --git a/pankoclient/osc.py b/pankoclient/osc.py deleted file mode 100644 index 21661fa..0000000 --- a/pankoclient/osc.py +++ /dev/null @@ -1,65 +0,0 @@ -# Copyright 2017 Huawei, Inc. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -import logging - -from osc_lib import utils - -from pankoclient.common.i18n import _ - -LOG = logging.getLogger(__name__) - -DEFAULT_EVENT_API_VERSION = '2' -API_VERSION_OPTION = 'os_event_api_version' -API_NAME = 'event' -API_VERSIONS = { - '2': 'pankoclient.v2.client.Client', -} - - -def make_client(instance): - """Returns an event service client""" - panko_client = utils.get_client_class( - API_NAME, - instance._api_version[API_NAME], - API_VERSIONS) - LOG.debug('Instantiating event client: %s', panko_client) - - endpoint = instance.get_endpoint_for_service_type( - API_NAME, - region_name=instance.region_name, - interface=instance.interface, - ) - kwargs = {'session': instance.session, - 'endpoint_override': endpoint} - - client = panko_client(**kwargs) - - return client - - -def build_option_parser(parser): - """Hook to add global options""" - parser.add_argument( - '--os-event-api-version', - metavar='', - default=utils.env( - 'OS_EVENT_API_VERSION', - default=DEFAULT_EVENT_API_VERSION), - help=(_('Event API version, default=%s ' - '(Env: OS_EVENT_API_VERSION)') % - DEFAULT_EVENT_API_VERSION) - ) - return parser diff --git a/pankoclient/shell.py b/pankoclient/shell.py deleted file mode 100644 index aa05d60..0000000 --- a/pankoclient/shell.py +++ /dev/null @@ -1,170 +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 logging -import os -import sys -import warnings - -from cliff import app -from cliff import commandmanager -from keystoneauth1 import exceptions -from keystoneauth1 import loading - -from pankoclient import __version__ -from pankoclient import client -from pankoclient.v2 import capabilities_cli -from pankoclient.v2 import events_cli - - -class PankoCommandManager(commandmanager.CommandManager): - SHELL_COMMANDS = { - "event list": events_cli.EventList, - "event show": events_cli.EventShow, - "event type list": events_cli.EventTypeList, - "event trait list": events_cli.EventTraitList, - "event trait description": events_cli.EventTraitDescription, - "capabilities list": capabilities_cli.CliCapabilitiesList, - } - - def load_commands(self, namespace): - for name, command_class in self.SHELL_COMMANDS.items(): - self.add_command(name, command_class) - - -class PankoShell(app.App): - def __init__(self): - super(PankoShell, self).__init__( - description='Panko command line client', - version=__version__, - command_manager=PankoCommandManager(None), - deferred_help=True, - ) - - self._client = None - - def build_option_parser(self, description, version): - """Return an argparse option parser for this application. - - Subclasses may override this method to extend - the parser with more global options. - :param description: full description of the application - :paramtype description: str - :param version: version number for the application - :paramtype version: str - """ - parser = super(PankoShell, self).build_option_parser(description, - version) - # Global arguments, one day this should go to keystoneauth1 - parser.add_argument( - '--os-region-name', - metavar='', - dest='region_name', - default=os.environ.get('OS_REGION_NAME'), - help='Authentication region name (Env: OS_REGION_NAME)') - parser.add_argument( - '--os-interface', - metavar='', - dest='interface', - choices=['admin', 'public', 'internal'], - default=os.environ.get('OS_INTERFACE'), - help='Select an interface type.' - ' Valid interface types: [admin, public, internal].' - ' (Env: OS_INTERFACE)') - parser.add_argument( - '--panko-api-version', - default=os.environ.get('PANKO_API_VERSION', '2'), - help='Defaults to env[PANKO_API_VERSION] or 2.') - parser.add_argument( - '--panko-endpoint', - metavar='', - dest='endpoint', - default=os.environ.get('PANKO_ENDPOINT'), - help='Aodh endpoint (Env: PANKO_ENDPOINT)') - loading.register_session_argparse_arguments(parser=parser) - loading.register_auth_argparse_arguments( - parser=parser, argv=sys.argv, default="password") - - return parser - - @property - def client(self): - # NOTE(sileht): we lazy load the client to not - # load/connect auth stuffs - if self._client is None: - if hasattr(self.options, "endpoint"): - endpoint_override = self.options.endpoint - else: - endpoint_override = None - auth_plugin = loading.load_auth_from_argparse_arguments( - self.options) - session = loading.load_session_from_argparse_arguments( - self.options, auth=auth_plugin) - - self._client = client.Client(self.options.panko_api_version, - session=session, - interface=self.options.interface, - region_name=self.options.region_name, - endpoint_override=endpoint_override) - return self._client - - def clean_up(self, cmd, result, err): - if isinstance(err, exceptions.HttpError) and err.details: - print(err.details, file=sys.stderr) - - def configure_logging(self): - if self.options.debug: - # --debug forces verbose_level 3 - # Set this here so cliff.app.configure_logging() can work - self.options.verbose_level = 3 - - super(PankoShell, self).configure_logging() - root_logger = logging.getLogger('') - - # Set logging to the requested level - if self.options.verbose_level == 0: - # --quiet - root_logger.setLevel(logging.ERROR) - warnings.simplefilter("ignore") - elif self.options.verbose_level == 1: - # This is the default case, no --debug, --verbose or --quiet - root_logger.setLevel(logging.WARNING) - warnings.simplefilter("ignore") - elif self.options.verbose_level == 2: - # One --verbose - root_logger.setLevel(logging.INFO) - warnings.simplefilter("once") - elif self.options.verbose_level >= 3: - # Two or more --verbose - root_logger.setLevel(logging.DEBUG) - - # Hide some useless message - requests_log = logging.getLogger("requests") - cliff_log = logging.getLogger('cliff') - stevedore_log = logging.getLogger('stevedore') - iso8601_log = logging.getLogger("iso8601") - - cliff_log.setLevel(logging.ERROR) - stevedore_log.setLevel(logging.ERROR) - iso8601_log.setLevel(logging.ERROR) - - if self.options.debug: - requests_log.setLevel(logging.DEBUG) - else: - requests_log.setLevel(logging.ERROR) - - -def main(args=None): - if args is None: - args = sys.argv[1:] - return PankoShell().run(args) diff --git a/pankoclient/tests/__init__.py b/pankoclient/tests/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/pankoclient/tests/functional/__init__.py b/pankoclient/tests/functional/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/pankoclient/tests/functional/base.py b/pankoclient/tests/functional/base.py deleted file mode 100644 index caf6eb2..0000000 --- a/pankoclient/tests/functional/base.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright 2016 Huawei, Inc. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from osc_lib.tests import utils - - -class TestBase(utils.TestCommand): - """Test case base class for all functional tests.""" - pass diff --git a/pankoclient/tests/unit/__init__.py b/pankoclient/tests/unit/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/pankoclient/tests/unit/base.py b/pankoclient/tests/unit/base.py deleted file mode 100644 index f480a0c..0000000 --- a/pankoclient/tests/unit/base.py +++ /dev/null @@ -1,34 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright 2016 Huawei, Inc. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from osc_lib.tests import utils - -from pankoclient.tests.unit import fakes - - -class TestBase(utils.TestCommand): - """Test case base class for all unit tests.""" - pass - - -class TestEventV2(TestBase): - """Test case base class for the unit tests of Event server V1 API.""" - - def setUp(self): - super(TestEventV2, self).setUp() - - fake_client = fakes.FakeTestEventV2Client() - self.app.client_manager.event = fake_client diff --git a/pankoclient/tests/unit/common/__init__.py b/pankoclient/tests/unit/common/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/pankoclient/tests/unit/common/test_base.py b/pankoclient/tests/unit/common/test_base.py deleted file mode 100644 index f167c8a..0000000 --- a/pankoclient/tests/unit/common/test_base.py +++ /dev/null @@ -1,277 +0,0 @@ -# Copyright 2016 Huawei, Inc. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -import copy -from unittest import mock - -import six - -from pankoclient.common import base -from pankoclient import exceptions -from pankoclient.tests.unit import base as test_base -from pankoclient.tests.unit import fakes - - -class TestResource(test_base.TestBase): - - def test_resource_repr(self): - r = base.Resource(None, dict(foo='bar', baz='spam')) - self.assertEqual('', repr(r)) - - def test_getid(self): - self.assertEqual(4, base.getid(4)) - - class TmpObject(object): - uuid = 4 - self.assertEqual(4, base.getid(TmpObject)) - - def test_init_with_attribute_info(self): - r = base.Resource(None, dict(foo='bar', baz='spam')) - self.assertTrue(hasattr(r, 'foo')) - self.assertEqual('bar', r.foo) - self.assertTrue(hasattr(r, 'baz')) - self.assertEqual('spam', r.baz) - - def test_resource_lazy_getattr(self): - fake_manager = mock.Mock() - return_resource = base.Resource(None, dict(uuid=mock.sentinel.fake_id, - foo='bar', - name='fake_name')) - fake_manager.get.return_value = return_resource - - r = base.Resource(fake_manager, - dict(uuid=mock.sentinel.fake_id, foo='bar')) - self.assertTrue(hasattr(r, 'foo')) - self.assertEqual('bar', r.foo) - self.assertFalse(r.is_loaded()) - - # Trigger load - self.assertEqual('fake_name', r.name) - fake_manager.get.assert_called_once_with(mock.sentinel.fake_id) - self.assertTrue(r.is_loaded()) - - # Missing stuff still fails after a second get - self.assertRaises(AttributeError, getattr, r, 'blahblah') - - def test_eq(self): - # Two resources of the same type with the same id: not equal - r1 = base.Resource(None, {'id': 1, 'name': 'hi'}) - r2 = base.Resource(None, {'id': 1, 'name': 'hello'}) - self.assertNotEqual(r1, r2) - - # Two resources of different types: never equal - r1 = base.Resource(None, {'id': 1}) - r2 = fakes.FakeResource(None, {'id': 1}) - self.assertNotEqual(r1, r2) - - # Two resources with no ID: equal if their info is equal - r1 = base.Resource(None, {'name': 'joe', 'age': 12}) - r2 = base.Resource(None, {'name': 'joe', 'age': 12}) - self.assertEqual(r1, r2) - - def test_resource_object_with_request_ids(self): - resp_obj = fakes.create_response_obj_with_header() - r = base.Resource(None, {'name': '1'}, resp=resp_obj) - self.assertEqual(fakes.FAKE_REQUEST_ID_LIST, r.request_ids) - - def test_resource_object_with_compute_request_ids(self): - resp_obj = fakes.create_response_obj_with_compute_header() - r = base.Resource(None, {'name': '1'}, resp=resp_obj) - self.assertEqual(fakes.FAKE_REQUEST_ID_LIST, r.request_ids) - - -class TestManager(test_base.TestBase): - fake_manager = fakes.create_resource_manager() - - @mock.patch.object(fakes.FakeHTTPClient, 'get') - def test_manager_get(self, mock_get): - mock_get.return_value = (fakes.create_response_obj_with_header(), - mock.MagicMock()) - fake_resource = fakes.FakeResource( - None, dict(uuid=fakes.FAKE_RESOURCE_ID, - name=fakes.FAKE_RESOURCE_NAME)) - result = self.fake_manager.get(fake_resource) - self.assertIsInstance(result, base.Resource) - self.assertIsInstance(result._info, mock.MagicMock) - self.assertTrue(result.is_loaded()) - expect_url = (fakes.FAKE_RESOURCE_ITEM_URL % fakes.FAKE_RESOURCE_ID) - mock_get.assert_called_once_with(expect_url, headers={}) - - @mock.patch.object(fakes.FakeHTTPClient, 'get') - def test_manager_list(self, mock_get): - mock_get.return_value = (fakes.create_response_obj_with_header(), - mock.MagicMock()) - result = self.fake_manager.list() - self.assertIsInstance(result, base.ListWithMeta) - self.assertEqual([], result) - expect_url = fakes.FAKE_RESOURCE_COLLECTION_URL - mock_get.assert_called_once_with(expect_url, headers={}) - - @mock.patch.object(fakes.FakeHTTPClient, 'patch') - def test_manager_update(self, mock_patch): - mock_patch.return_value = (fakes.create_response_obj_with_header(), - mock.MagicMock()) - fake_resource = fakes.FakeResource( - None, dict(uuid=fakes.FAKE_RESOURCE_ID, - name=fakes.FAKE_RESOURCE_NAME)) - result = self.fake_manager.update(fake_resource) - self.assertIsInstance(result, base.Resource) - self.assertIsInstance(result._info, mock.MagicMock) - self.assertFalse(result.is_loaded()) - expect_url = (fakes.FAKE_RESOURCE_ITEM_URL % fakes.FAKE_RESOURCE_ID) - mock_patch.assert_called_once_with(expect_url, data=fake_resource, - headers={}) - - @mock.patch.object(fakes.FakeHTTPClient, 'delete') - def test_manager_delete(self, mock_delete): - mock_delete.return_value = (fakes.create_response_obj_with_header(), - None) - fake_resource = fakes.FakeResource( - None, dict(uuid=fakes.FAKE_RESOURCE_ID, - name=fakes.FAKE_RESOURCE_NAME)) - result = self.fake_manager.delete(fake_resource) - self.assertIsInstance(result, base.TupleWithMeta) - self.assertEqual(tuple(), result) - expect_url = (fakes.FAKE_RESOURCE_ITEM_URL % fakes.FAKE_RESOURCE_ID) - mock_delete.assert_called_once_with(expect_url, headers={}) - - @mock.patch.object(fakes.FakeHTTPClient, 'post') - def test_manager_create(self, mock_post): - mock_post.return_value = (fakes.create_response_obj_with_header(), - mock.MagicMock()) - fake_resource = fakes.FakeResource( - None, dict(uuid=fakes.FAKE_RESOURCE_ID, - name=fakes.FAKE_RESOURCE_NAME)) - result = self.fake_manager.create(fake_resource) - self.assertIsInstance(result, base.Resource) - self.assertIsInstance(result._info, mock.MagicMock) - self.assertFalse(result.is_loaded()) - expect_url = fakes.FAKE_RESOURCE_COLLECTION_URL - mock_post.assert_called_once_with(expect_url, data=fake_resource, - headers={}) - - @mock.patch.object(fakes.FakeHTTPClient, 'get') - def test_manager_find(self, mock_get): - fake_json_body_1 = dict(uuid=fakes.FAKE_RESOURCE_ID, - name=fakes.FAKE_RESOURCE_NAME) - fake_json_body_2 = dict(uuid='no_existed_id', - name='no_existed_name') - mock_get.side_effect = [ - (fakes.create_response_obj_with_header(), - {'resources': [fake_json_body_1, - fake_json_body_2]}), - (fakes.create_response_obj_with_header(), - fake_json_body_1) - ] - result = self.fake_manager.find(uuid=fakes.FAKE_RESOURCE_ID, - name=fakes.FAKE_RESOURCE_NAME) - self.assertIsInstance(result, base.Resource) - self.assertEqual(fakes.FAKE_RESOURCE_ID, result.uuid) - self.assertEqual(fakes.FAKE_RESOURCE_NAME, result.name) - self.assertTrue(result.is_loaded()) - expect_collection_url = fakes.FAKE_RESOURCE_COLLECTION_URL - expect_item_url = (fakes.FAKE_RESOURCE_ITEM_URL % - fakes.FAKE_RESOURCE_ID) - mock_get.assert_has_calls( - [mock.call(expect_collection_url, headers={}), - mock.call(expect_item_url, headers={})]) - - @mock.patch.object(fakes.FakeHTTPClient, 'get') - def test_manager_find_no_result(self, mock_get): - mock_get.return_value = (fakes.create_response_obj_with_header(), - {'resources': []}) - self.assertRaises(exceptions.NotFound, - self.fake_manager.find, - uuid=fakes.FAKE_RESOURCE_ID, - name=fakes.FAKE_RESOURCE_NAME) - expect_collection_url = fakes.FAKE_RESOURCE_COLLECTION_URL - mock_get.assert_called_once_with(expect_collection_url, headers={}) - - @mock.patch.object(fakes.FakeHTTPClient, 'get') - def test_manager_find_more_than_one_result(self, mock_get): - fake_json_body_1 = dict(uuid=fakes.FAKE_RESOURCE_ID, - name=fakes.FAKE_RESOURCE_NAME) - fake_json_body_2 = copy.deepcopy(fake_json_body_1) - mock_get.return_value = (fakes.create_response_obj_with_header(), - {'resources': [fake_json_body_1, - fake_json_body_2]}) - self.assertRaises(exceptions.NoUniqueMatch, - self.fake_manager.find, - uuid=fakes.FAKE_RESOURCE_ID, - name=fakes.FAKE_RESOURCE_NAME) - expect_collection_url = fakes.FAKE_RESOURCE_COLLECTION_URL - mock_get.assert_called_once_with(expect_collection_url, headers={}) - - -class ListWithMetaTest(test_base.TestBase): - def test_list_with_meta(self): - resp = fakes.create_response_obj_with_header() - obj = base.ListWithMeta([], resp) - self.assertEqual([], obj) - # Check request_ids attribute is added to obj - self.assertTrue(hasattr(obj, 'request_ids')) - self.assertEqual(fakes.FAKE_REQUEST_ID_LIST, obj.request_ids) - - -class DictWithMetaTest(test_base.TestBase): - def test_dict_with_meta(self): - resp = fakes.create_response_obj_with_header() - obj = base.DictWithMeta({}, resp) - self.assertEqual({}, obj) - # Check request_ids attribute is added to obj - self.assertTrue(hasattr(obj, 'request_ids')) - self.assertEqual(fakes.FAKE_REQUEST_ID_LIST, obj.request_ids) - - -class TupleWithMetaTest(test_base.TestBase): - def test_tuple_with_meta(self): - resp = fakes.create_response_obj_with_header() - expected_tuple = (1, 2) - obj = base.TupleWithMeta(expected_tuple, resp) - self.assertEqual(expected_tuple, obj) - # Check request_ids attribute is added to obj - self.assertTrue(hasattr(obj, 'request_ids')) - self.assertEqual(fakes.FAKE_REQUEST_ID_LIST, obj.request_ids) - - -class StrWithMetaTest(test_base.TestBase): - def test_str_with_meta(self): - resp = fakes.create_response_obj_with_header() - obj = base.StrWithMeta('test-str', resp) - self.assertEqual('test-str', obj) - # Check request_ids attribute is added to obj - self.assertTrue(hasattr(obj, 'request_ids')) - self.assertEqual(fakes.FAKE_REQUEST_ID_LIST, obj.request_ids) - - -class BytesWithMetaTest(test_base.TestBase): - def test_bytes_with_meta(self): - resp = fakes.create_response_obj_with_header() - obj = base.BytesWithMeta(b'test-bytes', resp) - self.assertEqual(b'test-bytes', obj) - # Check request_ids attribute is added to obj - self.assertTrue(hasattr(obj, 'request_ids')) - self.assertEqual(fakes.FAKE_REQUEST_ID_LIST, obj.request_ids) - - -if six.PY2: - class UnicodeWithMetaTest(test_base.TestBase): - def test_unicode_with_meta(self): - resp = fakes.create_response_obj_with_header() - obj = base.UnicodeWithMeta(u'test-unicode', resp) - self.assertEqual(u'test-unicode', obj) - # Check request_ids attribute is added to obj - self.assertTrue(hasattr(obj, 'request_ids')) - self.assertEqual(fakes.FAKE_REQUEST_ID_LIST, obj.request_ids) diff --git a/pankoclient/tests/unit/fakes.py b/pankoclient/tests/unit/fakes.py deleted file mode 100644 index 7498a2f..0000000 --- a/pankoclient/tests/unit/fakes.py +++ /dev/null @@ -1,148 +0,0 @@ -# Copyright 2016 Huawei, Inc. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -from unittest import mock - -from oslo_serialization import jsonutils -from requests import Response - -from pankoclient.common import base -from pankoclient.v2 import capabilities -from pankoclient.v2 import events - - -# fake request id -FAKE_REQUEST_ID = 'req-0594c66b-6973-405c-ae2c-43fcfc00f2e3' -FAKE_REQUEST_ID_LIST = [FAKE_REQUEST_ID] - -# fake resource id -FAKE_RESOURCE_ID = '0594c66b-6973-405c-ae2c-43fcfc00f2e3' -FAKE_RESOURCE_NAME = 'name-0594c66b-6973-405c-ae2c-43fcfc00f2e3' - -# fake resource response key -FAKE_RESOURCE_ITEM_URL = '/resources/%s' -FAKE_RESOURCE_COLLECTION_URL = '/resources' - - -def create_response_obj_with_header(): - resp = Response() - resp.headers['x-openstack-request-id'] = FAKE_REQUEST_ID - return resp - - -def create_response_obj_with_compute_header(): - resp = Response() - resp.headers['x-compute-request-id'] = FAKE_REQUEST_ID - return resp - - -def create_resource_manager(): - return FakeManager() - - -class FakeTestEventV2Client(object): - - def __init__(self, **kwargs): - self.fake_http_client = mock.Mock() - self.capabilities = capabilities.CapabilitiesManager( - self.fake_http_client) - self.event_type = events.EventTypeManager(self.fake_http_client) - self.event_trait = events.EventTraitsManager(self.fake_http_client) - self.event_trait_description = events.EventTraitDescriptionManager( - self.fake_http_client) - - -class FakeHTTPClient(object): - - def get(self): - pass - - def head(self): - pass - - def post(self): - pass - - def put(self): - pass - - def delete(self): - pass - - def patch(self): - pass - - -class FakeResource(base.Resource): - pass - - -class FakeManager(base.ManagerWithFind): - resource_class = FakeResource - - def __init__(self, api=None): - if not api: - api = FakeHTTPClient() - super(FakeManager, self).__init__(api) - - def get(self, resource): - return self._get(FAKE_RESOURCE_ITEM_URL % base.getid(resource)) - - def list(self): - return self._list(FAKE_RESOURCE_COLLECTION_URL, - response_key='resources') - - def update(self, resource): - return self._update(FAKE_RESOURCE_ITEM_URL % base.getid(resource), - resource) - - def create(self, resource): - return self._create(FAKE_RESOURCE_COLLECTION_URL, resource) - - def delete(self, resource): - return self._delete(FAKE_RESOURCE_ITEM_URL % base.getid(resource)) - - -class FakeRaw(object): - version = 110 - - -class FakeHTTPResponse(object): - - version = 1.1 - - def __init__(self, status_code, reason, headers, content): - self.headers = headers - self.content = content - self.status_code = status_code - self.reason = reason - self.raw = FakeRaw() - - def getheader(self, name, default=None): - return self.headers.get(name, default) - - def getheaders(self): - return self.headers.items() - - def read(self, amt=None): - b = self.content - self.content = None - return b - - def iter_content(self, chunksize): - return self.content - - def json(self): - return jsonutils.loads(self.content) diff --git a/pankoclient/tests/unit/test_osc_plugin.py b/pankoclient/tests/unit/test_osc_plugin.py deleted file mode 100644 index 49fc4a2..0000000 --- a/pankoclient/tests/unit/test_osc_plugin.py +++ /dev/null @@ -1,73 +0,0 @@ -# Copyright 2016 Huawei, Inc. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -from unittest import mock - -from osc_lib.tests import fakes - -from pankoclient import osc as plugin -from pankoclient.tests.unit import base - - -class TestEventPlugin(base.TestBase): - - @mock.patch('pankoclient.v2.client.Client') - def test_make_client_with_session(self, panko_client): - instance = mock.Mock() - instance._api_version = { - plugin.API_NAME: plugin.DEFAULT_EVENT_API_VERSION} - instance.get_endpoint_for_service_type.return_value = mock.sentinel.ep - instance.region_name = fakes.REGION_NAME - instance.interface = fakes.INTERFACE - instance.auth.auth_url = fakes.AUTH_URL - instance.auth_ref.username = fakes.USERNAME - instance.session = 'fake_session' - - plugin.make_client(instance) - - instance.get_endpoint_for_service_type.assert_called_once_with( - plugin.API_NAME, - region_name=fakes.REGION_NAME, - interface=fakes.INTERFACE, - ) - panko_client.assert_called_once_with( - endpoint_override=mock.sentinel.ep, - session='fake_session', - ) - - @mock.patch('pankoclient.v2.client.Client') - def test_make_client_no_session(self, panko_client): - instance = mock.Mock() - instance._api_version = { - plugin.API_NAME: plugin.DEFAULT_EVENT_API_VERSION} - instance.get_endpoint_for_service_type.return_value = mock.sentinel.ep - instance.region_name = fakes.REGION_NAME - instance.interface = fakes.INTERFACE - instance.auth.auth_url = fakes.AUTH_URL - instance.auth_ref.username = fakes.USERNAME - instance.auth_ref.auth_token = fakes.AUTH_TOKEN - instance.session = None - - plugin.make_client(instance) - - instance.get_endpoint_for_service_type.assert_called_once_with( - plugin.API_NAME, - region_name=fakes.REGION_NAME, - interface=fakes.INTERFACE, - ) - panko_client.assert_called_once_with( - endpoint_override=mock.sentinel.ep, - session=None - ) diff --git a/pankoclient/tests/unit/v2/__init__.py b/pankoclient/tests/unit/v2/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/pankoclient/tests/unit/v2/test_events.py b/pankoclient/tests/unit/v2/test_events.py deleted file mode 100644 index c39cba8..0000000 --- a/pankoclient/tests/unit/v2/test_events.py +++ /dev/null @@ -1,75 +0,0 @@ -# Copyright 2017 Huawei, Inc. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -from unittest import mock - -from pankoclient.tests.unit import base as test_base -from pankoclient.v2 import events as events_mgr -from pankoclient.v2 import events_cli as events - - -@mock.patch.object(events_mgr.EventTypeManager, '_list') -class TestEventTypeList(test_base.TestEventV2): - def setUp(self): - super(TestEventTypeList, self).setUp() - self.cmd = events.EventTypeList(self.app, None) - - def test_event_type_list(self, mock_list): - arglist = [] - verifylist = [] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.cmd.take_action(parsed_args) - mock_list.assert_called_once_with('/v2/event_types/') - - -@mock.patch.object(events_mgr.EventTraitsManager, '_list') -class TestEventTraitsList(test_base.TestEventV2): - def setUp(self): - super(TestEventTraitsList, self).setUp() - self.cmd = events.EventTraitList(self.app, None) - - def test_event_traits_list(self, mock_list): - arglist = [ - 'event_type1', - 'trait_name1' - ] - verifylist = [ - ('type_name', 'event_type1'), - ('trait_name', 'trait_name1'), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.cmd.take_action(parsed_args) - expected_url = '/v2/event_types/%s/traits/%s' % ( - parsed_args.type_name, parsed_args.trait_name) - mock_list.assert_called_once_with(expected_url) - - -@mock.patch.object(events_mgr.EventTraitDescriptionManager, '_list') -class TestEventTraitDescription(test_base.TestEventV2): - def setUp(self): - super(TestEventTraitDescription, self).setUp() - self.cmd = events.EventTraitDescription(self.app, None) - - def test_event_type_traits_description(self, mock_list): - arglist = [ - 'event_type1', - ] - verifylist = [ - ('type_name', 'event_type1'), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.cmd.take_action(parsed_args) - expected_url = '/v2/event_types/%s/traits/' % parsed_args.type_name - mock_list.assert_called_once_with(expected_url) diff --git a/pankoclient/utils.py b/pankoclient/utils.py deleted file mode 100644 index b40ec5d..0000000 --- a/pankoclient/utils.py +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright 2017 Huawei, Inc. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# -from six.moves.urllib import parse as urllib_parse - - -def get_pagination_options(limit=None, marker=None, sorts=None): - options = [] - if limit: - options.append("limit=%d" % limit) - if marker: - options.append("marker=%s" % urllib_parse.quote(marker)) - for sort in sorts or []: - options.append("sort=%s" % urllib_parse.quote(sort)) - return "&".join(options) - - -def filtersdict_to_url(filters): - urls = [] - for k, v in sorted(filters.items()): - url = "q.field=%s&q.op=eq&q.value=%s" % (k, v) - urls.append(url) - return '&'.join(urls) diff --git a/pankoclient/v2/__init__.py b/pankoclient/v2/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/pankoclient/v2/capabilities.py b/pankoclient/v2/capabilities.py deleted file mode 100644 index 39237cb..0000000 --- a/pankoclient/v2/capabilities.py +++ /dev/null @@ -1,27 +0,0 @@ -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from pankoclient.common import base - - -class Capabilities(base.Resource): - pass - - -class CapabilitiesManager(base.ManagerWithFind): - resource_class = Capabilities - - def list(self): - """List capabilities""" - cap_url = "v2/capabilities/" - return self._get(cap_url) diff --git a/pankoclient/v2/capabilities_cli.py b/pankoclient/v2/capabilities_cli.py deleted file mode 100644 index cb86a0b..0000000 --- a/pankoclient/v2/capabilities_cli.py +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright 2016 Huawei, Inc. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -"""Panko v2 Capabilities action implementations""" - -from osc_lib.command import command - - -class CliCapabilitiesList(command.ShowOne): - """List capabilities for event service""" - - def take_action(self, parsed_args): - ac = self.app.client_manager.event - caps = ac.capabilities.list() - return self.dict2columns(caps._info) diff --git a/pankoclient/v2/client.py b/pankoclient/v2/client.py deleted file mode 100644 index 19338e1..0000000 --- a/pankoclient/v2/client.py +++ /dev/null @@ -1,40 +0,0 @@ -# Copyright 2017 Huawei, Inc. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -from pankoclient import client -from pankoclient.exceptions import ClientException -from pankoclient.v2 import capabilities -from pankoclient.v2 import events - - -class Client(object): - """Client for the Panko v2 API - - :param string session: session - :type session: :py:class:`keystoneauth.adapter.Adapter` - """ - - def __init__(self, session=None, service_type='event', **kwargs): - """Initialize a new client for the Panko v2 API.""" - if session is None: - raise ClientException(message='Session is required argument') - self.http_client = client.SessionClient( - session, service_type=service_type, **kwargs) - self.capabilities = capabilities.CapabilitiesManager(self.http_client) - self.event = events.EventManager(self.http_client) - self.event_type = events.EventTypeManager(self.http_client) - self.event_trait = events.EventTraitsManager(self.http_client) - self.event_trait_description = events.EventTraitDescriptionManager( - self.http_client) diff --git a/pankoclient/v2/events.py b/pankoclient/v2/events.py deleted file mode 100644 index f276799..0000000 --- a/pankoclient/v2/events.py +++ /dev/null @@ -1,89 +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 pankoclient.common import base -from pankoclient import utils - - -class Event(base.Resource): - pass - - -class EventType(base.Resource): - pass - - -class EventTrait(base.Resource): - pass - - -class TraitDescription(base.Resource): - pass - - -class EventManager(base.ManagerWithFind): - resource_class = Event - - def list(self, filters=None, limit=None, marker=None, sorts=None): - """Return all events matching the query filters. - - :param query: Filter arguments for which Events to return - :param limit: Maximum number of samples to be returned. - :param sorts: A pair of sort key and sort direction combined with ":" - :param marker: The pagination query marker, message id of the last - item viewed - """ - pagination = utils.get_pagination_options(limit, marker, sorts) - filter_string = (utils.filtersdict_to_url(filters) if - filters else "") - url = "v2/events" - options = [] - if filter_string: - options.append(filter_string) - if pagination: - options.append(pagination) - if options: - url += "?" + "&".join(options) - return self._list(url) - - def get(self, message_id): - """Return a single event with the given message id. - - :param message_id: Message ID of the Event to be returned - """ - path = '/v2/events/%s' - return self._get(path % message_id) - - -class EventTypeManager(base.ManagerWithFind): - resource_class = EventType - - def list(self): - url = '/v2/event_types/' - return self._list(url) - - -class EventTraitsManager(base.ManagerWithFind): - resource_class = EventTrait - - def list(self, event_type, trait_name): - url = '/v2/event_types/%s/traits/%s' % (event_type, trait_name) - return self._list(url) - - -class EventTraitDescriptionManager(base.ManagerWithFind): - resource_class = TraitDescription - - def list(self, event_type): - url = '/v2/event_types/%s/traits/' % event_type - return self._list(url) diff --git a/pankoclient/v2/events_cli.py b/pankoclient/v2/events_cli.py deleted file mode 100644 index aaf3ed2..0000000 --- a/pankoclient/v2/events_cli.py +++ /dev/null @@ -1,153 +0,0 @@ -# Copyright 2016 Huawei, Inc. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -"""Panko v2 event action implementations""" -import copy - -from osc_lib.command import command -from osc_lib import utils -from oslo_serialization import jsonutils - - -class EventList(command.Lister): - """List events""" - - @staticmethod - def split_filter_param(param): - key, eq_op, value = param.partition('=') - if not eq_op: - msg = 'Malformed parameter(%s). Use the key=value format.' % param - raise ValueError(msg) - return key, value - - def get_parser(self, prog_name): - parser = super(EventList, self).get_parser(prog_name) - parser.add_argument('--filter', dest='filter', - metavar='=', - type=self.split_filter_param, - action='append', - help='Filter parameters to apply on returned ' - 'events. (can be applied multiple times)') - parser.add_argument("--limit", type=int, metavar="", - help="Number of events to return " - "(Default is server default)") - parser.add_argument("--marker", metavar="", - help="Last item of the previous listing. " - "Return the next results after this value," - "the supported marker is message_id.") - parser.add_argument("--sort", action="append", - metavar="", - help="Sort of events attribute, " - "e.g. name:asc") - return parser - - def take_action(self, parsed_args): - ac = self.app.client_manager.event - filters = dict(parsed_args.filter) if parsed_args.filter else None - events = ac.event.list( - filters=filters, sorts=parsed_args.sort, - limit=parsed_args.limit, marker=parsed_args.marker) - columns = ('event_type', 'generated', 'message_id', 'traits:name', - 'traits:value', 'traits:type') - rows = [] - for event in events: - traits_type = [t['type'] for t in event.traits] - traits_name = [t['name'] for t in event.traits] - traits_value = [t['value'] for t in event.traits] - [getattr(event, item, '') for item in columns] - row = (getattr(event, 'event_type', ''), - getattr(event, 'generated', ''), - getattr(event, 'message_id', ''), - '\n'.join(traits_name), - '\n'.join(traits_value), - '\n'.join(traits_type),) - rows.append(row) - return columns, tuple(rows) - - -class EventShow(command.ShowOne): - """List events""" - - def get_parser(self, prog_name): - parser = super(EventShow, self).get_parser(prog_name) - parser.add_argument( - 'message_id', - metavar='', - help="event of specified message_id to display" - ) - return parser - - def take_action(self, parsed_args): - ac = self.app.client_manager.event - event = ac.event.get(message_id=parsed_args.message_id) - data = copy.deepcopy(event._info) - data.update({'traits': jsonutils.dumps(data['traits'], indent=4)}) - return self.dict2columns(data) - - -class EventTypeList(command.Lister): - """List event types""" - - def take_action(self, parsed_args): - ac = self.app.client_manager.event - event_types = ac.event_type.list() - return ('Event Type',), ((t,)for t in event_types) - - -class EventTraitList(command.Lister): - """List event traits of a specified event type and trait name""" - - def get_parser(self, prog_name): - parser = super(EventTraitList, self).get_parser(prog_name) - parser.add_argument( - 'type_name', - metavar='', - help='Type of the event for which traits will listed.' - ) - parser.add_argument( - 'trait_name', - metavar='', - help='The name of the trait to list.' - ) - return parser - - def take_action(self, parsed_args): - ac = self.app.client_manager.event - event_traits = ac.event_trait.list( - parsed_args.type_name, parsed_args.trait_name) - columns = ('name', 'value', 'type') - return (columns, - (utils.get_item_properties(t, columns) for t in event_traits)) - - -class EventTraitDescription(command.Lister): - """List trait info for an event type.""" - - def get_parser(self, prog_name): - parser = super(EventTraitDescription, self).get_parser(prog_name) - parser.add_argument( - 'type_name', - metavar='', - help='Type of the event for which traits definitions will be ' - 'shown.' - ) - return parser - - def take_action(self, parsed_args): - ac = self.app.client_manager.event - event_traits = ac.event_trait_description.list(parsed_args.type_name) - columns = ('name', 'type') - return (columns, - (utils.get_item_properties(t, columns) for t in event_traits)) diff --git a/releasenotes/notes/.placeholder b/releasenotes/notes/.placeholder deleted file mode 100644 index e69de29..0000000 diff --git a/releasenotes/notes/drop-py-2-7-08bf464fcf679d24.yaml b/releasenotes/notes/drop-py-2-7-08bf464fcf679d24.yaml deleted file mode 100644 index c1f7be0..0000000 --- a/releasenotes/notes/drop-py-2-7-08bf464fcf679d24.yaml +++ /dev/null @@ -1,6 +0,0 @@ ---- -upgrade: - - | - Python 2.7 support has been dropped. Last release of python-pankoclient - to support py2.7 is OpenStack Train. The minimum version of Python now - supported by python-pankoclient is Python 3.6. diff --git a/releasenotes/source/_static/.placeholder b/releasenotes/source/_static/.placeholder deleted file mode 100644 index e69de29..0000000 diff --git a/releasenotes/source/_templates/.placeholder b/releasenotes/source/_templates/.placeholder deleted file mode 100644 index e69de29..0000000 diff --git a/releasenotes/source/conf.py b/releasenotes/source/conf.py deleted file mode 100644 index 3ac9776..0000000 --- a/releasenotes/source/conf.py +++ /dev/null @@ -1,283 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright 2017 Huawei, Inc. All rights reserved. - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Panko Client Release Notes documentation build configuration file, created -# by sphinx-quickstart on Tue Nov 3 17:40:50 2015. -# -# This file is execfile()d with the current directory set to its -# containing dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -# sys.path.insert(0, os.path.abspath('.')) - -# -- General configuration ------------------------------------------------ - -# If your documentation needs a minimal Sphinx version, state it here. -# needs_sphinx = '1.0' - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [ - 'openstackdocstheme', - 'reno.sphinxext', -] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# The suffix of source filenames. -source_suffix = '.rst' - -# The encoding of source files. -# source_encoding = 'utf-8-sig' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -project = u'pankoclient Release Notes' -copyright = u'2016, OpenStack Foundation' - -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -# The short X.Y version. -# The full version, including alpha/beta/rc tags. -release = '' -# The short X.Y version. -version = '' - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -# language = None - -# There are two options for replacing |today|: either, you set today to some -# non-false value, then it is used: -# today = '' -# Else, today_fmt is used as the format for a strftime call. -# today_fmt = '%B %d, %Y' - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -exclude_patterns = [] - -# The reST default role (used for this markup: `text`) to use for all -# documents. -# default_role = None - -# If true, '()' will be appended to :func: etc. cross-reference text. -# add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -# add_module_names = True - -# If true, sectionauthor and moduleauthor directives will be shown in the -# output. They are ignored by default. -# show_authors = False - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'native' - -# A list of ignored prefixes for module index sorting. -# modindex_common_prefix = [] - -# If true, keep warnings as "system message" paragraphs in the built documents. -# keep_warnings = False - - -# -- Options for HTML output ---------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -html_theme = 'openstackdocs' - -# openstackdocstheme options -openstackdocs_repo_name = 'openstack/python-pankoclient' -openstackdocs_bug_project = 'python-pankoclient' -openstackdocs_bug_tag = '' -openstackdocs_auto_name = False - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -# html_theme_options = {} - -# Add any paths that contain custom themes here, relative to this directory. -# html_theme_path = [] - -# The name for this set of Sphinx documents. If None, it defaults to -# " v documentation". -# html_title = None - -# A shorter title for the navigation bar. Default is the same as html_title. -# html_short_title = None - -# The name of an image file (relative to this directory) to place at the top -# of the sidebar. -# html_logo = None - -# The name of an image file (within the static path) to use as favicon of the -# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -# html_favicon = None - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] - -# Add any extra paths that contain custom files (such as robots.txt or -# .htaccess) here, relative to this directory. These files are copied -# directly to the root of the documentation. -# html_extra_path = [] - -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -# html_use_smartypants = True - -# Custom sidebar templates, maps document names to template names. -# html_sidebars = {} - -# Additional templates that should be rendered to pages, maps page names to -# template names. -# html_additional_pages = {} - -# If false, no module index is generated. -# html_domain_indices = True - -# If false, no index is generated. -# html_use_index = True - -# If true, the index is split into individual pages for each letter. -# html_split_index = False - -# If true, links to the reST sources are added to the pages. -# html_show_sourcelink = True - -# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -# html_show_sphinx = True - -# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -# html_show_copyright = True - -# If true, an OpenSearch description file will be output, and all pages will -# contain a tag referring to it. The value of this option must be the -# base URL from which the finished HTML is served. -# html_use_opensearch = '' - -# This is the file name suffix for HTML files (e.g. ".xhtml"). -# html_file_suffix = None - -# Output file base name for HTML help builder. -htmlhelp_basename = 'PankoClientReleaseNotesdoc' - - -# -- Options for LaTeX output --------------------------------------------- - -latex_elements = { - # The paper size ('letterpaper' or 'a4paper'). - # 'papersize': 'letterpaper', - - # The font size ('10pt', '11pt' or '12pt'). - # 'pointsize': '10pt', - - # Additional stuff for the LaTeX preamble. - # 'preamble': '', -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, -# author, documentclass [howto, manual, or own class]). -latex_documents = [ - ('index', 'PankoClientReleaseNotes.tex', - u'Panko Client Release Notes Documentation', - u'Panko Client Developers', 'manual'), -] - -# The name of an image file (relative to this directory) to place at the top of -# the title page. -# latex_logo = None - -# For "manual" documents, if this is true, then toplevel headings are parts, -# not chapters. -# latex_use_parts = False - -# If true, show page references after internal links. -# latex_show_pagerefs = False - -# If true, show URL addresses after external links. -# latex_show_urls = False - -# Documents to append as an appendix to all manuals. -# latex_appendices = [] - -# If false, no module index is generated. -# latex_domain_indices = True - - -# -- Options for manual page output --------------------------------------- - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [ - ('index', 'pankoclientreleasenotes', - u'Panko Client Release Notes Documentation', - [u'Panko Client Developers'], 1) -] - -# If true, show URL addresses after external links. -# man_show_urls = False - - -# -- Options for Texinfo output ------------------------------------------- - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) -texinfo_documents = [ - ('index', 'PankoClientReleaseNotes', - u'Panko Client Release Notes Documentation', - u'Panko Client Developers', 'PankoClientReleaseNotes', - 'One line description of project.', - 'Miscellaneous'), -] - -# Documents to append as an appendix to all manuals. -# texinfo_appendices = [] - -# If false, no module index is generated. -# texinfo_domain_indices = True - -# How to display URL addresses: 'footnote', 'no', or 'inline'. -# texinfo_show_urls = 'footnote' - -# If true, do not generate a @detailmenu in the "Top" node's menu. -# texinfo_no_detailmenu = False - -# -- Options for Internationalization output ------------------------------ -locale_dirs = ['locale/'] diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst deleted file mode 100644 index 434f918..0000000 --- a/releasenotes/source/index.rst +++ /dev/null @@ -1,15 +0,0 @@ -================================ -python-pankoclient Release Notes -================================ - -.. toctree:: - :maxdepth: 1 - - unreleased - wallaby - victoria - ussuri - train - stein - queens - pike diff --git a/releasenotes/source/pike.rst b/releasenotes/source/pike.rst deleted file mode 100644 index e43bfc0..0000000 --- a/releasenotes/source/pike.rst +++ /dev/null @@ -1,6 +0,0 @@ -=================================== - Pike Series Release Notes -=================================== - -.. release-notes:: - :branch: stable/pike diff --git a/releasenotes/source/queens.rst b/releasenotes/source/queens.rst deleted file mode 100644 index 36ac616..0000000 --- a/releasenotes/source/queens.rst +++ /dev/null @@ -1,6 +0,0 @@ -=================================== - Queens Series Release Notes -=================================== - -.. release-notes:: - :branch: stable/queens diff --git a/releasenotes/source/stein.rst b/releasenotes/source/stein.rst deleted file mode 100644 index efaceb6..0000000 --- a/releasenotes/source/stein.rst +++ /dev/null @@ -1,6 +0,0 @@ -=================================== - Stein Series Release Notes -=================================== - -.. release-notes:: - :branch: stable/stein diff --git a/releasenotes/source/train.rst b/releasenotes/source/train.rst deleted file mode 100644 index 5839003..0000000 --- a/releasenotes/source/train.rst +++ /dev/null @@ -1,6 +0,0 @@ -========================== -Train Series Release Notes -========================== - -.. release-notes:: - :branch: stable/train diff --git a/releasenotes/source/unreleased.rst b/releasenotes/source/unreleased.rst deleted file mode 100644 index 875030f..0000000 --- a/releasenotes/source/unreleased.rst +++ /dev/null @@ -1,5 +0,0 @@ -============================ -Current Series Release Notes -============================ - -.. release-notes:: diff --git a/releasenotes/source/ussuri.rst b/releasenotes/source/ussuri.rst deleted file mode 100644 index e21e50e..0000000 --- a/releasenotes/source/ussuri.rst +++ /dev/null @@ -1,6 +0,0 @@ -=========================== -Ussuri Series Release Notes -=========================== - -.. release-notes:: - :branch: stable/ussuri diff --git a/releasenotes/source/victoria.rst b/releasenotes/source/victoria.rst deleted file mode 100644 index 4efc7b6..0000000 --- a/releasenotes/source/victoria.rst +++ /dev/null @@ -1,6 +0,0 @@ -============================= -Victoria Series Release Notes -============================= - -.. release-notes:: - :branch: stable/victoria diff --git a/releasenotes/source/wallaby.rst b/releasenotes/source/wallaby.rst deleted file mode 100644 index d77b565..0000000 --- a/releasenotes/source/wallaby.rst +++ /dev/null @@ -1,6 +0,0 @@ -============================ -Wallaby Series Release Notes -============================ - -.. release-notes:: - :branch: stable/wallaby diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 0ba2e2b..0000000 --- a/requirements.txt +++ /dev/null @@ -1,13 +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. - -keystoneauth1>=3.4.0 # Apache-2.0 -osc-lib>=1.8.0 # Apache-2.0 -oslo.i18n>=2.1.0 # Apache-2.0 -oslo.serialization>=1.10.0 # Apache-2.0 -oslo.utils>=3.18.0 # Apache-2.0 -osprofiler>=1.4.0 # Apache-2.0 -pbr>=1.8 # Apache-2.0 -requests>=2.14.2 # Apache-2.0 -six>=1.9.0 # MIT diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index b86112e..0000000 --- a/setup.cfg +++ /dev/null @@ -1,55 +0,0 @@ -[metadata] -name = pankoclient -summary = Python client library for OpenStack Panko project. -description-file = - README.rst -license = Apache License, Version 2.0 -author = OpenStack -author-email = openstack-discuss@lists.openstack.org -home-page = https://docs.openstack.org/python-pankoclient/latest/ -python-requires = >=3.6 -classifier = - Development Status :: 5 - Production/Stable - Environment :: Console - 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 = - pankoclient - -[extras] -test = - coverage>=4.0 # Apache-2.0 - hacking>=3.0,<3.1.0 # Apache-2.0 - python-openstackclient>=3.3.0 # Apache-2.0 - oslotest>=1.10.0 # Apache-2.0 - reno>=1.8.0 # Apache-2.0 - requests-mock>=1.1 # Apache-2.0 - testrepository>=0.0.18 # Apache-2.0/BSD - testscenarios>=0.4 # Apache-2.0/BSD - -[entry_points] -console_scripts = - panko = pankoclient.shell:main - -openstack.cli.extension = - event = pankoclient.osc - -openstack.event.v2 = - event capabilities list = pankoclient.v2.capabilities_cli:CliCapabilitiesList - event list = pankoclient.v2.events_cli:EventList - event show = pankoclient.v2.events_cli:EventShow - event type list = pankoclient.v2.events_cli:EventTypeList - event trait list = pankoclient.v2.events_cli:EventTraitList - event trait description = pankoclient.v2.events_cli:EventTraitDescription diff --git a/setup.py b/setup.py deleted file mode 100644 index 0cdc8c2..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>=1.8'], - pbr=True) diff --git a/tox.ini b/tox.ini deleted file mode 100644 index 953110b..0000000 --- a/tox.ini +++ /dev/null @@ -1,47 +0,0 @@ -[tox] -minversion = 3.1.1 -envlist = py38,pep8 -skipsdist = True -ignore_basepython_conflict = True - -[testenv] -basepython = python3 -usedevelop = True -install_command = pip install -c{env:UPPER_CONSTRAINTS_FILE:https://opendev.org/openstack/requirements/raw/branch/master/upper-constraints.txt} {opts} {packages} -setenv = - VIRTUAL_ENV={envdir} -deps = .[test] -commands = python setup.py test --slowest --testr-args='{posargs}' - -[testenv:pep8] -commands = flake8 {posargs} - -[testenv:venv] -# Used by openstack-infra to build the doc -deps = -r{toxinidir}/doc/requirements.txt -commands = {posargs} - -[testenv:cover] -commands = - python setup.py test --coverage --testr-args='{posargs}' - coverage report - -[testenv:docs] -deps = -r{toxinidir}/doc/requirements.txt -commands = - rm -rf doc/source/ref - sphinx-build -W -b html doc/source doc/build/html - -[testenv:releasenotes] -commands = - sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html - -[testenv:debug] -commands = oslo_debug_helper -t pankoclient/tests {posargs} - -[flake8] -show-source = True -# W504 line break after binary operator -ignore = W504 -builtins = _ -exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build