Retire Packaging Deb project repos

This commit is part of a series to retire the Packaging Deb
project. Step 2 is to remove all content from the project
repos, replacing it with a README notification where to find
ongoing work, and how to recover the repo if needed at some
future point (as in
https://docs.openstack.org/infra/manual/drivers.html#retiring-a-project).

Change-Id: I1531aed08fc23dbcb04550285f9d41993cd10af4
This commit is contained in:
Tony Breeds 2017-09-12 16:04:06 -06:00
parent a67ed2f235
commit ace5ca6578
50 changed files with 14 additions and 3728 deletions

View File

@ -1,7 +0,0 @@
[run]
branch = True
source = os_api_ref
omit = os_api_ref/openstack/*
[report]
ignore_errors = True

55
.gitignore vendored
View File

@ -1,55 +0,0 @@
*.py[cod]
# C extensions
*.so
# Packages
*.egg*
*.egg-info
dist
build
eggs
parts
bin
var
sdist
develop-eggs
.installed.cfg
lib
lib64
# Installer logs
pip-log.txt
# Unit test / coverage reports
cover/
.coverage*
!.coveragerc
.tox
nosetests.xml
.testrepository
.venv
# Translations
*.mo
# Mr Developer
.mr.developer.cfg
.project
.pydevproject
# Complexity
output/*.html
output/*/index.html
# Sphinx
doc/build
# pbr generates these
AUTHORS
ChangeLog
# Editors
*~
.*.swp
.*sw?

View File

@ -1,4 +0,0 @@
[gerrit]
host=review.openstack.org
port=29418
project=openstack/os-api-ref.git

View File

@ -1,3 +0,0 @@
# Format is:
# <preferred e-mail> <other e-mail 1>
# <preferred e-mail> <other e-mail 2>

View File

@ -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

View File

@ -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/openstack-doc-tools

View File

@ -1,4 +0,0 @@
os-api-ref Style Commandments
===============================================
Read the OpenStack Style Commandments http://docs.openstack.org/developer/hacking/

176
LICENSE
View File

@ -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.

View File

@ -1,6 +0,0 @@
include AUTHORS
include ChangeLog
exclude .gitignore
exclude .gitreview
global-exclude *.pyc

14
README Normal file
View File

@ -0,0 +1,14 @@
This project is no longer maintained.
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".
For ongoing work on maintaining OpenStack packages in the Debian
distribution, please see the Debian OpenStack packaging team at
https://wiki.debian.org/OpenStack/.
For any further questions, please email
openstack-dev@lists.openstack.org or join #openstack-dev on
Freenode.

View File

@ -1,112 +0,0 @@
========================
Team and repository tags
========================
.. image:: http://governance.openstack.org/badges/os-api-ref.svg
:target: http://governance.openstack.org/reference/tags/index.html
.. Change things from this point on
os-api-ref
==========
Sphinx Extensions to support API reference sites in OpenStack
This project is a collection of sphinx stanzas that assist in building
an API Reference site for an OpenStack project in RST. RST is great
for unstructured English, but displaying semi structured (and
repetitive) data in tables is not its strength. This provides tooling
to insert semi-structured data describing request and response
parameters and status or error messages, and turn those into nice tables.
The project also includes a set of styling (and javascript) that is
expected to layer on top of a Sphinx theme base. This addition
provides a nice set of collapsing sections for REST methods and
javascript controls to expand / collapse all sections.
Features
--------
* Sphinx stanza ``rest_method`` describing the method and resource for a REST
API call. Lets authors write simply and also gives readers a clean way to
scan all methods then click a button to get more information about a method.
* Sphinx stanza ``rest_parameters`` used to insert semi-structured data into
the RST files describing the parameters users can send with the request. The
stanza points to a structured YAML file, ``parameters.yaml``.
* Sphinx stanza ``rest_status_code`` used to insert pointers to error or status
codes returned by the service. Points to a structured YAML file,
``http_codes.yaml``.
TODO
----
A list, in no particular order, of things we should do in this
project. If you would like to contribute to any of these please show
up in ``#openstack-dev`` on IRC and ask for ``sdague`` or ``mugsie``
to discuss or send an email to the openstack-dev@lists.openstack.org list
with [api] in the subject line.
* Enhance documentation with more examples and best practices
* Testing for the code
* ``max_microversion`` parameter support - so that we automatically
tag parameters that have been removed
* Make a microversion selector, so that you can get a version of the api-ref
that hides all microversion elements beyond your selected version
(this one is going to be a bit of complex javascript), in progress.
Potential ideas
~~~~~~~~~~~~~~~
These aren't even quite todos, but just ideas about things that might
be useful.
* ``.. literalinclude`` is good for API samples files to be included,
but should we have more markup that includes the full ``REST /URL``
as well.
Sphinx stanzas
--------------
**rest_method**: Enter the REST method, such as GET, PUT, POST, DELETE,
followed by the resource (not including an endpoint) for the call. For
example::
.. rest_method:: PUT /v2/images/{image_id}/file
**rest_parameters**: Enter a reference to a ``parameters.yaml`` file and
indicate which parameter you want to document. For example::
.. rest_parameters:: images-parameters.yaml
- Content-type: Content-Type-data
- image_id: image_id-in-path
Where the ``images-parameters.yaml`` file contains pointers named
``Content-type`` and ``image_id`` and descriptions for each.
**rest_status_code**: Enter a reference to a ``http-status.yaml`` file and
indicate which errors or status codes you want to document. You can also add
a pointer to more precise descriptions for each code. For example::
.. rest_status_code:: success http-codes.yaml
- 204
.. rest_status_code:: error http-codes.yaml
- 400: informal
- 401
- 403
- 404
- 409
- 410: image-data-410
- 413: image-data-413
- 415: image-data-415
- 500: informal
- 503: image-data-503
* Free software: Apache license
* Documentation: http://docs.openstack.org/developer/os-api-ref
* Source: http://git.openstack.org/cgit/openstack/os-api-ref

View File

@ -1,41 +0,0 @@
===========
Releasing
===========
This documents the how and when to release ``os-api-ref``.
When to release
===============
Any time there are fixes or additions ready to go, they should be
released. Releases are cheap. If it's been more than a month and there
are changes in master, consider releasing them.
If the changes are entirely on the CSS / JS cosmetic side, things are
usually pretty safe to release as long as they have been spot checked
against a couple of projects. (The gate does the nova tree
automatically).
If new warnings are added
-------------------------
If **new** warnings have been added since the last release, care
should be taken to:
* Alert the mailing list 2 days before the release about the new
warning coming in (that should give them time to go non enforcing
or fix the issue).
* Ensure that you bump at least the Y in the version number
(X.Y.Z). New warnings are not a Z level release.
How to release
==============
Check out ``openstack/releases``
Edit ``deliverables/_independent/os-api-ref.yaml``
Add a line with the version number desired, the git has of the commit
that should be that release.
If you have questions ask in ``#openstack-release``

View File

@ -1,14 +0,0 @@
==============================
Tests that should be written
==============================
Negative
* test warning when groups are out of order
* test when parameter keys are missing
* test when description RST is invalid
* test when parameter file is missing
Microversion
* test attributes are set when microversions are annotated.

View File

@ -1,2 +0,0 @@
[python: **.py]

View File

@ -1,74 +0,0 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os
import sys
sys.path.insert(0, os.path.abspath('../..'))
# -- General configuration ----------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = [
'sphinx.ext.autodoc',
#'sphinx.ext.intersphinx',
'oslosphinx'
]
# 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'os-api-ref'
copyright = u'2016, The contributors'
# 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 = 'sphinx'
# -- 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']
# 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}

View File

@ -1,4 +0,0 @@
Contributing
============
.. include:: ../../CONTRIBUTING.rst

View File

@ -1,63 +0,0 @@
200:
default: |
Request was successful.
image-data-200: |
The service lists the image data in the response body.
201:
default: |
Request has been fulfilled and new resource created.
202:
default: |
Request is accepted, but processing may take some time.
203:
default: |
Returned information is not full set, but a subset.
204:
default: |
Request fulfilled but service does not return anything.
300:
default: |
The resource corresponds to more than one representation.
400:
default: |
Some content in the request was invalid.
401:
default: |
User must authenticate before making a request.
403:
default: |
Policy does not allow current user to do this operation.
404:
default: |
The requested resource could not be found.
405:
default: |
Method is not valid for this endpoint and resource.
409:
default: |
This resource has an action in progress that would conflict with this request.
413:
default: |
This operation cannot be completed.
image-data-413: |
The payload cannot be accepted. Possible causes include:
* The backend storage is full.
* This request added to your existing image data exceeds your total
storage quota for images.
* The image payload submitted with this request exceeds the maximum
allowable image size.
415:
default: |
The entity of the request is in a format not supported by the requested
resource for the method.
500:
default: |
Something went wrong with the service which prevents it from fulfilling
the request.
501:
default: |
The service does not have the functionality required to fulfill this
request.
503:
default: |
The service cannot handle the request right now.

View File

@ -1,23 +0,0 @@
.. os-api-ref 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.
==============================================
Welcome to os-api-ref developer documentation!
==============================================
.. toctree::
:maxdepth: 1
installation
usage
contributing
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

View File

@ -1,11 +0,0 @@
Installation
============
At the command line::
$ pip install os-api-ref
Or, if you have virtualenvwrapper installed::
$ mkvirtualenv os-api-ref
$ pip install os-api-ref

View File

@ -1,340 +0,0 @@
Usage
=====
``os-api-ref`` is designed to be used inside of a sphinx tree that is
devoted solely to the documentation of the API.
Modify your ``source/conf.py`` file to include ``os_api_ref`` in the
list of sphinx extensions. This extension assumes you are also using
``openstackdocstheme`` for some of the styling, and may not fully work if you
are not.
.. code-block:: python
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = [
'os_api_ref',
]
Stanzas
=======
rest_method
-----------
The ``rest_method`` stanza is a way to declare that a section is about
a particular REST method. It takes the form of:
.. code-block:: rst
.. rest_method:: <METHODNAME> <url>
``METHODNAME`` should be one of the commonly used REST methods or HTTP verbs.
This stanza should be the first element in a ``section`` that has some
descriptive title about the method. An example from the Nova
documentation is:
.. code-block:: rst
List Servers
============
.. rest_method:: GET /v2.1/{tenant_id}/servers
Lists IDs, names, and links for all servers.
Servers contain a status attribute that indicates the current server
state. You can filter on the server status when you complete a list
servers request. The server status is returned in the response
body. The possible server status values are:
...
This is going to do a slightly unexpected transform where the
``rest_method`` is pivoted up and into the section title to produce an
HTML line of the form: <METHOD> <URL> <SECTIONTITLE> <SHOW/HIDE
BUTTON>.
The entire contents of the ``List Servers`` section will then be
hidden by default, with a button to open it on demand.
rest_parameters
---------------
The ``rest_parameters`` stanza is a solution to the problem of tables
in ``rst``.
A REST API that uses JSON has a large number of structured parameters
that include type, location (i.e. is this in the query, the header,
the path, the body), whether or not this parameter is required, as well as
the desire to provide a long description about each parameter. And, assuming
some consistent modeling, that parameter will show up in multiple calls. A
``server_id`` used in the path is always going to have the same
meaning.
It is natural to want to display this data in a tabular way to show
all these dimensions. However, tables in ``rst`` are quite cumbersome, and
repeating the same data over and over again is error prone.
The ``rest_parameters`` stanza solves this by having the inline markup
be a yaml list of ``name: value`` pairs. ``name`` is the name of the
parameter. ``value`` is the key to lookup the rest of the details for
this parameter in a parameters file. In each method,
there should be one ``rest_parameters`` stanza for the request, and
another ``rest_parameters`` stanza for the response.
.. code-block:: rst
.. rest_parameters:: parameters.yaml
- tenant_id: tenant_id
- changes-since: changes-since
- image: image_query
- flavor: flavor_query
- name: server_name_query
- status: server_status_query
- host: host_query
- limit: limit
- marker: marker
And corresponding entries in ``parameters.yaml``:
.. code-block:: yaml
tenant_id:
description: |
The UUID of the tenant in a multi-tenancy cloud.
in: path
required: true
type: string
...
changes-since:
description: |
Filters the response by a date and time when the image last changed status.
Use this query parameter to check for changes since a previous request rather
than re-downloading and re-parsing the full status at each polling interval.
If data has changed, the call returns only the items changed since the ``changes-since``
time. If data has not changed since the ``changes-since`` time, the call returns an
empty list.\nTo enable you to keep track of changes, this filter also displays images
that were deleted if the ``changes-since`` value specifies a date in the last 30 days.
Items deleted more than 30 days ago might be returned, but it is not guaranteed.
The date and time stamp format is `ISO 8601 <https://en.wikipedia.org/wiki/ISO_8601>`_:
::
CCYY-MM-DDThh:mm:ss±hh:mm
The ``±hh:mm`` value, if included, returns the time zone as an offset from UTC.
For example, ``2015-08-27T09:49:58-05:00``.
If you omit the time zone, the UTC time zone is assumed.
in: query
required: false
type: string
server_status_query:
description: |
Filters the response by a server status, as a string. For example, ``ACTIVE``.
in: query
required: false
type: string
Every ``rest_parameters`` stanza specifies the lookup file it will
use. This gives you the freedom to decide how you would like to split
up your parameters, ranging from a single global file, to a dedicated
file for every stanza, or anywhere in between.
parameters file format
----------------------
The parameters file is inspired by the OpenAPI (aka: Swagger)
specification. The OpenAPI specification provides a property object
which categorizes the parameters by type and describes how the parameter is used.
The following fields exist for every entry:
in
where this parameter exists. One of ``header``, ``path``,
``query``, ``body``.
description
a free form description of the parameter. This can be
multiline (if using the | or > tags in yaml), and supports ``rst``
format syntax.
required
whether this parameter is required or not. If ``required:
false`` the parameter name will be rendered with an (Optional)
keyword next to it
type
the javascript/json type of the field. one of ``boolean``, ``int``,
``float``, ``string``, ``array``, ``object``.
min_version
the microversion that this parameter was introduced at. Will render
a *new in $version* stanza in the html output.
max_version
the last version that includes this parameter. Will render
a *Deprecated in $version* stanza in the html output.
rest_status_code
----------------
The ``rest_status_code`` stanza is how you can show what HTTP status codes your
API uses and what they indicate.
.. code-block:: rst
.. rest_status_code:: <success|error> <location of http-status.yaml file>
This stanza should be the first element after the narrative section of the
method description.
An example from the Designate documentation is:
.. code-block:: rst
:emphasize-lines: 11-25
Create Zone
===========
.. rest_method:: POST /v2/zones
Create a zone
Response codes
--------------
.. rest_status_code:: success status.yaml
- 200
- 100
- 201
.. rest_status_code:: error status.yaml
- 405
- 403
- 401
- 400
- 500
- 409: duplicate_zone
And corresponding entries in ``http-status.yaml``:
.. code-block:: yaml
100:
default: |
An unusual code for an API
200:
default: |
Request was successful.
201:
default: >
Request has been fulfilled and new resource created. The ``Location`` header
has the URL to the new item.
400:
default: |
Some content in the request was invalid
zone_data_error: |
Some of the data for the zone in the request is unavailable to the service.
401:
default: |
User must authenticate before making a request.
403:
default: |
Policy does not allow current user to do this operation.
405:
default: |
Method is not valid for this endpoint and resource.
409:
default: |
This resource has an action in progress that would conflict with this request.
duplicate_zone: |
There is already a zone with this name.
500:
default: |
Something went wrong with the service which prevents it from fulfilling the request.
This RST example creates two HTML tables of response codes, one for success and one for
errors.
status file format
------------------
This is a simple yaml file, with a single object of status codes and the
reasons that each would be used.
Each status code **must** have a default entry in the status yaml file. The default entry is used
in the ``rest_status_code`` stanza when a code is listed with no value or lookup key.
There may be situations where the reason for a code may be different across
endpoints, or a different message may be appropriate.
In this case, adding a entry at the same level as the ``default`` and
referencing that in the stanza like so:
.. code-block:: yaml
- 409: duplicate_zone
This will override the default message with the newly defined one.
You can get a copy of a starter status file from the os-api-ref repository,
by downloading :download:`http-status.yaml <http-status.yaml>`.
rest_expand_all
---------------
The ``rest_expand_all`` stanza is used to place a control in the
document that will be a global Show / Hide for all sections. There are
times when this is extremely nice to have.
Including Sample Files
======================
To refer to a sample file in a ``rst`` file, use the
``rst`` directive, ``literalinclude``. Typically, the content sent
or received is of type JSON, so the language role is set to javascript.
The example immediately follows the parameter listing in the ``rst`` file.
An example of an included Nova response sample file:
.. code-block:: rst
.. literalinclude:: ../../doc/api_samples/os-evacuate/server-evacuate-resp.json
:language: javascript
Runtime Warnings
================
The extension tries to help when it can by pointing out that something isn't
matching up correctly. The following warnings are generated when
issues are found:
* parameters file is not found
* parameters file is not valid yaml, i.e.
missing colon after the name
* a lookup value in the ``rst`` file is not found in the parameters file
* the parameters file is not sorted as outlined in the rules below
The sorting rules for parameters file is that first elements should be
sorted by ``in``, going from earliest to latest processed.
#. header
#. path
#. query
#. body
After that, the parameters should be sorted by name, lower case alpha
numerically.
The sort enforcement is because in large parameters files it helps
prevent unintended duplicates.

View File

@ -1,705 +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 collections import OrderedDict
import os
import re
from docutils import nodes
from docutils.parsers.rst.directives.tables import Table
from docutils.statemachine import ViewList
import pbr.version
from sphinx.util.compat import Directive
from sphinx.util.osutil import copyfile
import yaml
from os_api_ref.http_codes import http_code
from os_api_ref.http_codes import http_code_html
from os_api_ref.http_codes import HTTPResponseCodeDirective
__version__ = pbr.version.VersionInfo(
'os_api_ref').version_string()
# This is to allow for a graceful swap from oslosphinx to openstackdocstheme
THEME = 'openstackdocstheme'
"""This provides a sphinx extension able to create the HTML needed
for the api-ref website.
It contains 2 new stanzas.
.. rest_method:: GET /foo/bar
Which is designed to be used as the first stanza in a new section to
state that section is about that REST method. During processing the
rest stanza will be reparented to be before the section in question,
and used as a show/hide selector for it's details.
.. rest_parameters:: file.yaml
- name1: name_in_file1
- name2: name_in_file2
- name3: name_in_file3
Which is designed to build structured tables for either response or
request parameters. The stanza takes a value which is a file to lookup
details about the parameters in question.
The contents of the stanza are a yaml list of key / value pairs. The
key is the name of the parameter to be shown in the table. The value
is the key in the file.yaml where all other metadata about the
parameter will be extracted. This allows for reusing parameter
definitions widely in API definitions, but still providing for control
in both naming and ordering of parameters at every declaration.
"""
def ordered_load(
stream, Loader=yaml.SafeLoader, object_pairs_hook=OrderedDict):
"""Load yaml as an ordered dict
This allows us to inspect the order of the file on disk to make
sure it was correct by our rules.
"""
class OrderedLoader(Loader):
pass
def construct_mapping(loader, node):
loader.flatten_mapping(node)
return object_pairs_hook(loader.construct_pairs(node))
OrderedLoader.add_constructor(
yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG,
construct_mapping)
# for parameters.yaml we treat numbers (especially version
# numbers) as strings. So that microversion specification of 2.20
# and 2.2 don't get confused.
OrderedLoader.add_constructor(
u'tag:yaml.org,2002:float',
yaml.constructor.SafeConstructor.construct_yaml_str)
return yaml.load(stream, OrderedLoader)
class rest_method(nodes.Part, nodes.Element):
"""Node for rest_method stanza
Because we need to insert very specific HTML at the final stage of
processing, the rest_method stanza needs a custom node type. This
lets us accumulate the relevant data into this node, during
parsing, but not turn it into known sphinx types (lists, tables,
sections).
Then, during the final build phase we transform directly to the
html that we want.
NOTE: this means we error trying to build latex or man pages for
these stanza types right now. This is all fixable if we add an
output formatter for this node type, but it's not yet a
priority. Contributions welcomed.
"""
pass
class rest_expand_all(nodes.Part, nodes.Element):
"""A node placeholder for the expand all button.
This is a node that we can insert into the doctree which on final
render can be converted to the custom HTML we need for the expand
all button. It is automatically inserted at the top of the page
for API ref documents.
"""
pass
class RestExpandAllDirective(Directive):
# This tells sphinx that the directive will need to generate
# content during the final build phase.
has_content = True
def run(self):
app = self.state.document.settings.env.app
node = rest_expand_all()
max_ver = app.config.os_api_ref_max_microversion
min_ver = app.config.os_api_ref_min_microversion
releases = app.config.os_api_ref_release_microversions
node['major'] = None
try:
if max_ver.split('.')[0] == min_ver.split('.')[0]:
node['max_ver'] = int(max_ver.split('.')[1])
node['min_ver'] = int(min_ver.split('.')[1])
node['major'] = int(max_ver.split('.')[0])
node['releases'] = releases
except ValueError:
# TODO(sdague): warn that we're ignoring this all
pass
except IndexError:
pass
return [node]
class RestMethodDirective(Directive):
# this enables content in the directive
has_content = True
@staticmethod
def find_param(content, name):
for line in content:
if ("%s: " % name) in line:
_, value = line.split(': ')
return value.rstrip().lstrip()
return None
def run(self):
lineno = self.state_machine.abs_line_number()
target = nodes.target()
section = nodes.section(classes=["detail-control"])
# env = self.state.document.settings.env
# env.app.info("Parent %s" % self.state.parent.attributes)
node = rest_method()
# TODO(sdague): this is a super simplistic parser, should be
# more robust.
method, sep, url = self.content[0].partition(' ')
node['min_version'] = self.find_param(self.content, 'min_version')
node['max_version'] = self.find_param(self.content, 'max_version')
node['method'] = method
node['url'] = url
# Extract the path parameters from the url
env = self.state.document.settings.env
env.path_params = []
env.path_params = re.findall("{[a-zA-Z][a-zA-Z_0-9]*}", url)
node['target'] = self.state.parent.attributes['ids'][0]
node['css_classes'] = ""
if node['min_version']:
node['css_classes'] += "rp_min_ver_%s " % (
str(node['min_version']).replace('.', '_'))
if node['max_version']:
node['css_classes'] += "rp_max_ver_%s " % (
str(node['max_version']).replace('.', '_'))
# We need to build a temporary target that we can replace
# later in the processing to get the TOC to resolve correctly.
temp_target = "%s-selector" % node['target']
target = nodes.target(ids=[temp_target])
self.state.add_target(temp_target, '', target, lineno)
section += node
return [target, section]
# cache for file -> yaml so we only do the load and check of a yaml
# file once during a sphinx processing run.
YAML_CACHE = {}
class RestParametersDirective(Table):
headers = ["Name", "In", "Type", "Description"]
def _load_param_file(self, fpath):
global YAML_CACHE
if fpath in YAML_CACHE:
return YAML_CACHE[fpath]
lookup = {}
# self.app.info("Fpath: %s" % fpath)
try:
with open(fpath, 'r') as stream:
lookup = ordered_load(stream)
except IOError:
self.app.warn(
"Parameters file %s not found" % fpath,
(self.env.docname, None))
return
except yaml.YAMLError as exc:
self.app.warn(exc)
raise
if lookup:
self._check_yaml_sorting(fpath, lookup)
else:
self.app.warn(
"Parameters file is empty %s" % fpath,
(self.env.docname, None))
return
YAML_CACHE[fpath] = lookup
return lookup
def _check_yaml_sorting(self, fpath, yaml_data):
"""check yaml sorting
Assuming we got an ordered dict, we iterate through it
basically doing a gnome sort test
(https://en.wikipedia.org/wiki/Gnome_sort) and ensure the item
we are looking at is > the last item we saw. This is done at
the section level first, so we're grouped, then alphabetically
by lower case name within a section. Every time there is a
mismatch we raise an warn message.
"""
sections = {"header": 1, "path": 2, "query": 3, "body": 4}
last = None
for key, value in yaml_data.items():
# use of an invalid 'in' value
if value['in'] not in sections:
self.app.warn(
"``%s`` is not a valid value for 'in' (must be one of: %s)"
". (see ``%s``)" % (
value['in'],
", ".join(sorted(sections.keys())),
key)
)
continue
if last is None:
last = (key, value)
continue
# ensure that sections only go up
current_section = value['in']
last_section = last[1]['in']
if sections[current_section] < sections[last_section]:
self.app.warn(
"Section out of order. All parameters in section ``%s`` "
"should be after section ``%s``. (see ``%s``)" % (
last_section,
current_section,
last[0]))
if (sections[value['in']] == sections[last[1]['in']] and
key.lower() < last[0].lower()):
self.app.warn(
"Parameters out of order ``%s`` should be after ``%s``" % (
last[0], key))
last = (key, value)
def yaml_from_file(self, fpath):
"""Collect Parameter stanzas from inline + file.
This allows use to reference an external file for the actual
parameter definitions.
"""
lookup = self._load_param_file(fpath)
if not lookup:
return
content = "\n".join(self.content)
parsed = yaml.safe_load(content)
# self.app.info("Params loaded is %s" % parsed)
# self.app.info("Lookup table looks like %s" % lookup)
new_content = list()
for paramlist in parsed:
if not isinstance(paramlist, dict):
self.app.warn(
("Invalid parameter definition ``%s``. Expected "
"format: ``name: reference``. "
" Skipping." % paramlist),
(self.state_machine.node.source,
self.state_machine.node.line))
continue
for name, ref in paramlist.items():
if ref in lookup:
new_content.append((name, lookup[ref]))
else:
# TODO(sdague): this provides a kind of confusing
# error message because app.warn isn't meant to be
# used this way, however it does provide a way to
# track down where the parameters list is that is
# wrong. So it's good enough for now.
self.app.warn(
("No field definition for ``%s`` found in ``%s``. "
" Skipping." % (ref, fpath)),
(self.state_machine.node.source,
self.state_machine.node.line))
# Check for path params in stanza
for i, param in enumerate(self.env.path_params):
if (param.rstrip('}').lstrip('{')) == name:
del self.env.path_params[i]
break
else:
continue
if len(self.env.path_params) is not 0:
# Warn that path parameters are not set in rest_parameter
# stanza and will not appear in the generated table.
for param in self.env.path_params:
self.app.warn(
("No path parameter ``%s`` found in rest_parameter"
" stanza.\n"
% param.rstrip('}').lstrip('{')),
(self.state_machine.node.source,
self.state_machine.node.line))
# self.app.info("New content %s" % new_content)
self.yaml = new_content
def run(self):
self.env = self.state.document.settings.env
self.app = self.env.app
# Make sure we have some content, which should be yaml that
# defines some parameters.
if not self.content:
error = self.state_machine.reporter.error(
'No parameters defined',
nodes.literal_block(self.block_text, self.block_text),
line=self.lineno)
return [error]
if not len(self.arguments) >= 1:
error = self.state_machine.reporter.error(
'No reference file defined',
nodes.literal_block(self.block_text, self.block_text),
line=self.lineno)
return [error]
# NOTE(sdague): it's important that we pop the arg otherwise
# we end up putting the filename as the table caption.
rel_fpath, fpath = self.env.relfn2path(self.arguments.pop())
self.yaml_file = fpath
self.yaml_from_file(self.yaml_file)
self.max_cols = len(self.headers)
# TODO(sdague): it would be good to dynamically set column
# widths (or basically make the colwidth thing go away
# entirely)
self.options['widths'] = [20, 10, 10, 60]
self.col_widths = self.get_column_widths(self.max_cols)
if isinstance(self.col_widths, tuple):
# In docutils 0.13.1, get_column_widths returns a (widths,
# colwidths) tuple, where widths is a string (i.e. 'auto').
# See https://sourceforge.net/p/docutils/patches/120/.
self.col_widths = self.col_widths[1]
# Actually convert the yaml
title, messages = self.make_title()
# self.app.info("Title %s, messages %s" % (title, messages))
table_node = self.build_table()
self.add_name(table_node)
if title:
table_node.insert(0, title)
return [table_node] + messages
def get_rows(self, table_data):
rows = []
groups = []
trow = nodes.row()
entry = nodes.entry()
para = nodes.paragraph(text=unicode(table_data))
entry += para
trow += entry
rows.append(trow)
return rows, groups
# Add a column for a field. In order to have the RST inside
# these fields get rendered, we need to use the
# ViewList. Note, ViewList expects a list of lines, so chunk
# up our content as a list to make it happy.
def add_col(self, value):
entry = nodes.entry()
result = ViewList(value.split('\n'))
self.state.nested_parse(result, 0, entry)
return entry
def show_no_yaml_error(self):
trow = nodes.row(classes=["no_yaml"])
trow += self.add_col("No yaml found %s" % self.yaml_file)
trow += self.add_col("")
trow += self.add_col("")
trow += self.add_col("")
return trow
def collect_rows(self):
rows = []
groups = []
try:
# self.app.info("Parsed content is: %s" % self.yaml)
for key, values in self.yaml:
min_version = values.get('min_version', '')
max_version = values.get('max_version', '')
desc = values.get('description', '')
classes = []
if min_version:
desc += ("\n\n**New in version %s**\n" % min_version)
min_ver_css_name = ("rp_min_ver_" +
str(min_version).replace('.', '_'))
classes.append(min_ver_css_name)
if max_version:
desc += ("\n\n**Deprecated in version %s**\n" %
max_version)
max_ver_css_name = ("rp_max_ver_" +
str(max_version).replace('.', '_'))
classes.append(max_ver_css_name)
trow = nodes.row(classes=classes)
name = key
if values.get('required', False) is False:
name += " (Optional)"
trow += self.add_col(name)
trow += self.add_col(values.get('in'))
trow += self.add_col(values.get('type'))
trow += self.add_col(desc)
rows.append(trow)
except AttributeError as exc:
if 'key' in locals():
self.app.warn("Failure on key: %s, values: %s. %s" %
(key, values, exc))
else:
rows.append(self.show_no_yaml_error())
return rows, groups
def build_table(self):
table = nodes.table()
tgroup = nodes.tgroup(cols=len(self.headers))
table += tgroup
# TODO(sdague): it would be really nice to figure out how not
# to have this stanza, it kind of messes up all of the table
# formatting because it doesn't let tables just be the right
# size.
tgroup.extend(
nodes.colspec(colwidth=col_width, colname='c' + str(idx))
for idx, col_width in enumerate(self.col_widths)
)
thead = nodes.thead()
tgroup += thead
row_node = nodes.row()
thead += row_node
row_node.extend(nodes.entry(h, nodes.paragraph(text=h))
for h in self.headers)
tbody = nodes.tbody()
tgroup += tbody
rows, groups = self.collect_rows()
tbody.extend(rows)
table.extend(groups)
return table
def rest_method_html(self, node):
tmpl = """
<div class="operation-grp %(css_classes)s">
<div class="row">
<div class="col-md-2">
<div class="operation">
<a name="%(target)s" class="operation-anchor" href="#%(target)s"
onclick="window.location.hash = hash;"
>
<span class="glyphicon glyphicon-link"></span></a>
<span class="label label-%(method)s">%(method)s</span>
</div>
</div>
<div class="col-md-9">
<div class="row">
<div class="endpoint-container">
<div class="row col-md-12">%(url)s</div>
<div class="row col-md-12"><p class="url-subtitle">%(desc)s</p></div>
</div>
</div>
</div>
<div class="col-md-1">
<button
class="btn btn-info btn-sm btn-detail"
data-target="#%(target)s-detail"
data-toggle="collapse"
id="%(target)s-detail-btn"
>detail</button>
</div>
</div>
</div>"""
node['url'] = node['url'].replace(
'{',
'<span class="path_parameter">{')
node['url'] = node['url'].replace(
'}',
'}</span>')
self.body.append(tmpl % node)
raise nodes.SkipNode
def rest_expand_all_html(self, node):
tmpl = """
<div class="row">
%(extra_js)s
<div class="col-md-2 col-md-offset-9">
%(selector)s
</div>
<div class=col-md-1>
<button id="expand-all"
data-toggle="collapse"
class="btn btn-info btn-sm btn-expand-all"
>Show All</button>
</div>
</div>"""
node.setdefault('selector', "")
node.setdefault('extra_js', "")
if node['major']:
node['selector'], node['extra_js'] = create_mv_selector(node)
self.body.append(tmpl % node)
raise nodes.SkipNode
def create_mv_selector(node):
mv_list = '<option value="" selected="selected">All</option>'
for x in range(node['min_ver'], node['max_ver'] + 1):
mv_list += build_mv_item(node['major'], x, node['releases'])
selector_tmpl = """
<form class=form-inline">
<div class="form-group">
<label class="control-label">
Microversions
</label>
<select class="combobox form-control" id="mv_select">
%(mv_list)s
</select>
</div>
</form>
"""
js_tmpl = """
<script>
os_max_mv = %(max)d;
os_min_mv = %(min)d;
</script>
"""
selector_content = {
'mv_list': mv_list
}
js_content = {
'min': node['min_ver'],
'max': node['max_ver']
}
return selector_tmpl % selector_content, js_tmpl % js_content
def build_mv_item(major, micro, releases):
version = "%d.%d" % (major, micro)
if version in releases:
return '<option value="%s">%s - %s</option>' % (
version, version, releases[version].capitalize())
else:
return '<option value="%s">%s</option>' % (version, version)
def resolve_rest_references(app, doctree):
for node in doctree.traverse():
if isinstance(node, rest_method):
rest_node = node
rest_method_section = node.parent
rest_section = rest_method_section.parent
gp = rest_section.parent
# Added required classes to the top section
rest_section.attributes['classes'].append('api-detail')
rest_section.attributes['classes'].append('collapse')
# Pop the title off the collapsed section
title = rest_section.children.pop(0)
rest_node['desc'] = title.children[0]
# In order to get the links in the sidebar to be right, we
# have to do some id flipping here late in the game. The
# rest_method_section has basically had a dummy id up
# until this point just to keep it from colliding with
# it's parent.
rest_section.attributes['ids'][0] = (
"%s-detail" % rest_section.attributes['ids'][0])
rest_method_section.attributes['ids'][0] = rest_node['target']
# Pop the overall section into it's grand parent,
# right before where the current parent lives
idx = gp.children.index(rest_section)
rest_section.remove(rest_method_section)
gp.insert(idx, rest_method_section)
def copy_assets(app, exception):
assets = ('api-site.css', 'api-site.js', 'combobox.js')
fonts = (
'glyphicons-halflings-regular.ttf',
'glyphicons-halflings-regular.woff'
)
if app.builder.name != 'html' or exception:
return
app.info('Copying assets: %s' % ', '.join(assets))
app.info('Copying fonts: %s' % ', '.join(fonts))
for asset in assets:
dest = os.path.join(app.builder.outdir, '_static', asset)
source = os.path.abspath(os.path.dirname(__file__))
copyfile(os.path.join(source, 'assets', asset), dest)
for font in fonts:
dest = os.path.join(app.builder.outdir, '_static/fonts', font)
source = os.path.abspath(os.path.dirname(__file__))
copyfile(os.path.join(source, 'assets', font), dest)
def add_assets(app):
app.add_stylesheet('api-site.css')
app.add_javascript('api-site.js')
app.add_javascript('combobox.js')
def setup(app):
# Add some config options around microversions
app.add_config_value('os_api_ref_max_microversion', '', 'env')
app.add_config_value('os_api_ref_min_microversion', '', 'env')
app.add_config_value('os_api_ref_release_microversions', '', 'env')
# TODO(sdague): if someone wants to support latex/pdf, or man page
# generation using these stanzas, here is where you'd need to
# specify content specific renderers.
app.add_node(rest_method,
html=(rest_method_html, None))
app.add_node(rest_expand_all,
html=(rest_expand_all_html, None))
app.add_node(http_code,
html=(http_code_html, None))
# This specifies all our directives that we're adding
app.add_directive('rest_parameters', RestParametersDirective)
app.add_directive('rest_method', RestMethodDirective)
app.add_directive('rest_expand_all', RestExpandAllDirective)
app.add_directive('rest_status_code', HTTPResponseCodeDirective)
# The doctree-read hook is used do the slightly crazy doc
# transformation that we do to get the rest_method document
# structure.
app.connect('doctree-read', resolve_rest_references)
# Add all the static assets to our build during the early stage of building
app.connect('builder-inited', add_assets)
# This copies all the assets (css, js, fonts) over to the build
# _static directory during final build.
app.connect('build-finished', copy_assets)
return {'version': __version__}

View File

@ -1,218 +0,0 @@
tt.literal {
padding: 2px 4px;
font-size: 90%;
color: #c7254e;
white-space: nowrap;
background-color: #f9f2f4;
border-radius: 4px;
}
/* bootstrap users blockquote for pull quotes, so they are much
larger, we need them smaller */
blockquote { font-size: 1em; }
.docs-book-wrapper {
width: 90% !important
}
tbody>tr:nth-child(odd)>td,
tbody>tr:nth-child(odd)>th {
background-color: #f9f9f9;
}
td>p {
margin: 0 0 0.5em;
}
.operation-grp {
padding-top: 0.5em;
padding-bottom: 1em;
}
/* Ensure the method buttons and their links don't split lines when
the page is narrower */
.operation {
/* this moves the link icon into the gutter */
margin-left: -1.25em;
margin-right: 1.25em;
white-space: nowrap;
}
/* These make the links only show up on hover */
a.operation-anchor {
visibility: hidden;
}
.operation-grp:hover a.operation-anchor {
visibility: visible;
}
/* All parameter tables should be full width */
.api-detail table.docutils {
width: 100%;
}
.versionmodified {
font-weight: bold;
}
.label-POST {
background-color: #5cb85c;
}
.label-POST[href]:hover,
.label-POST[href]:focus {
background-color: #449d44;
}
.label-GET,
.label-HEAD {
background-color: #5bc0de;
}
.label-GET[href]:hover,
.label-GET[href]:focus,
.label-HEAD[href]:hover,
.label-HEAD[href]:focus {
background-color: #31b0d5;
}
.label-PUT,
.label-PATCH {
background-color: #f0ad4e;
}
.label-PUT[href]:hover,
.label-PUT[href]:focus,
.label-PATCH[href]:hover,
.label-PATCH[href]:focus {
background-color: #ec971f;
}
.label-COPY {
background-color: #6666ff;
}
.label-COPY[href]:hover,
.label-COPY[href]:focus {
background-color: #6699ff;
}
.label-DELETE {
background-color: #d9534f;
}
.label-DELETE[href]:hover,
.label-DELETE[href]:focus {
background-color: #c9302c;
}
.btn-detail:hover, .btn-detail:focus,
.btn-expand-all:hover, .btn-expand-all:focus {
color: #fff;
background-color: #3b6c91;
border-color: #269abc;
}
.btn-detail,
.btn-expand-all {
color: #fff;
background-color: #2A4E68;
}
span.path_parameter {
font-family: monospace;
padding: 2px 4px;
font-size: 90%;
color: #c7254e;
white-space: nowrap;
background-color: #f9f2f4;
border-radius: 4px;
}
/* for microversion selector */
.mv_selector {
font-size: 0.8em;
padding: 0.3em;
}
.mv_selector.active {
color: #fff;
background-color: #31b0d5;
border-color: #269abc;
}
p.url-subtitle {
color: #555;
font-weight: bold;
}
.docs-body .section h1 {
display: block;
}
div.docs-sidebar-toc > div > ul > li {
list-style-type: none;
font-size: 0.8em;
font-weight: bold;
}
div.docs-sidebar-toc > div > ul {
padding-left: 20px
}
div.docs-sidebar-toc > div > ul > li > ul > li {
list-style-type: disc;
font-weight: normal;
}
div.docs-top-contents {
display: none;
}
div.endpoint-container{
padding-left: 15px;
}
#expand-all {
margin-top: 23px;
}
### Combobox Experiment
@media (min-width: 768px) {
.form-search .combobox-container,
.form-inline .combobox-container {
display: inline-block;
margin-bottom: 0;
vertical-align: top;
}
.form-search .combobox-container .input-group-addon,
.form-inline .combobox-container .input-group-addon {
width: auto;
}
}
.combobox-selected .caret {
display: none;
}
/* :not doesn't work in IE8 */
.combobox-container:not(.combobox-selected) .glyphicon-remove {
display: none;
}
.typeahead-long {
max-height: 300px;
overflow-y: auto;
}
.control-group.error .combobox-container .add-on {
color: #B94A48;
border-color: #B94A48;
}
.control-group.error .combobox-container .caret {
border-top-color: #B94A48;
}
.control-group.warning .combobox-container .add-on {
color: #C09853;
border-color: #C09853;
}
.control-group.warning .combobox-container .caret {
border-top-color: #C09853;
}
.control-group.success .combobox-container .add-on {
color: #468847;
border-color: #468847;
}
.control-group.success .combobox-container .caret {
border-top-color: #468847;
}

View File

@ -1,178 +0,0 @@
(function() {
// the list of expanded element ids
var expanded = [];
// whether we should sync expand changes with the location
// url. We need to make this false during large scale
// operations because we're using the history API, which is
// expensive. So a bulk expand turns this off, expands
// everything, turns it back on, then does a history sync.
var should_sync = true;
$(document).ready(function() {
// Change the text on the expando buttons when
// appropriate. This also add or removes them to the list of
// expanded sections, and then syncs that list to the history
// after such a change.
$('.api-detail')
.on('hide.bs.collapse', function(e) {
processButton(this, 'detail');
var index = expanded.indexOf(this.id);
if (index > -1) {
expanded.splice(index, 1);
}
sync_expanded();
})
.on('show.bs.collapse', function(e) {
processButton(this, 'close');
expanded.push(this.id);
sync_expanded();
});
// Expand the world. Wires up the expand all button, it turns
// off the sync while it is running to save the costs with the
// history API.
var expandAllActive = true;
$('#expand-all').click(function () {
should_sync = false;
if (expandAllActive) {
expandAllActive = false;
$('.api-detail').collapse('show');
$('#expand-all').attr('data-toggle', '');
$(this).text('Hide All');
} else {
expandAllActive = true;
$('.api-detail').collapse('hide');
$('#expand-all').attr('data-toggle', 'collapse');
$(this).text('Show All');
}
should_sync = true;
sync_expanded();
});
// if there is an expanded parameter passed in a url, we run
// through and expand all the appropriate things.
if (window.location.search.substring(1).indexOf("expanded") > -1) {
should_sync = false;
var parts = window.location.search.substring(1).split('&');
for (var i = 0; i < parts.length; i++) {
var keyval = parts[i].split('=');
if (keyval[0] == "expanded" && keyval[1]) {
var expanded_ids = keyval[1].split(',');
for (var j = 0; j < expanded_ids.length; j++) {
$('#' + expanded_ids[j]).collapse('show');
}
}
}
should_sync = true;
// This is needed because the hash *might* be inside a
// collapsed section.
//
// NOTE(sdague): this doesn't quite seem to work while
// we're changing the rest of the document.
$(document.body).scrollTop($(window.location.hash).offset().top);
}
// Wire up microversion selector
$('.mv_selector').on('click', function(e) {
var version = e.currentTarget.innerHTML;
// flip what is active
$(this).addClass('active').siblings().removeClass('active');
if (version == "All") {
reset_microversion();
} else {
set_microversion(version);
}
});
});
/**
* Helper function for setting the text, styles for expandos
*/
function processButton(button, text) {
$('#' + $(button).attr('id') + '-btn').text(text)
.toggleClass('btn-info')
.toggleClass('btn-default');
}
// Take the expanded array and push it into history. Because
// sphinx is building css appropriate ids, they should not have
// any special characters we need to encode. So we can simply join
// them into a comma separated list.
function sync_expanded() {
if (should_sync) {
var url = UpdateQueryString('expanded', expanded.join(','));
history.pushState('', 'new expand', url);
}
}
// Generically update the query string for a url. Credit to
// http://stackoverflow.com/questions/5999118/add-or-update-query-string-parameter
// for making this properly generic.
function UpdateQueryString(key, value, url) {
if (!url) url = window.location.href;
var re = new RegExp("([?&])" + key + "=.*?(&|#|$)(.*)", "gi"),
hash;
if (re.test(url)) {
if (typeof value !== 'undefined' && value !== null)
return url.replace(re, '$1' + key + "=" + value + '$2$3');
else {
hash = url.split('#');
url = hash[0].replace(re, '$1$3').replace(/(&|\?)$/, '');
if (typeof hash[1] !== 'undefined' && hash[1] !== null)
url += '#' + hash[1];
return url;
}
}
else {
if (typeof value !== 'undefined' && value !== null) {
var separator = url.indexOf('?') !== -1 ? '&' : '?';
hash = url.split('#');
url = hash[0] + separator + key + '=' + value;
if (typeof hash[1] !== 'undefined' && hash[1] !== null)
url += '#' + hash[1];
return url;
}
else
return url;
}
}
// Set the Y value of the microversion to turn on / off visibility
// of components.
function set_microversion(number) {
var major = number.split(".")[0];
var micro = number.split(".")[1];
for (var i = os_min_mv; i <= os_max_mv; i++) {
var max_class = ".rp_max_ver_" + major + "_" + i;
var min_class = ".rp_min_ver_" + major + "_" + i;
if (i < micro) {
$(max_class).hide(400);
$(min_class).show(400);
} else if (i >= micro) {
$(min_class).hide(400);
$(max_class).show(400);
}
}
}
function reset_microversion() {
$('[class^=rp_min_ver]').show(400);
$('[class^=rp_max_ver]').show(400);
}
$(document).ready(function(){
$('#mv_select').combobox({appendId: '-visable'});
$('#mv_select').on('change', function() {
var version = this.value;
if (version == "") {
reset_microversion();
} else {
set_microversion(version);
}
});
});
})();

View File

@ -1,462 +0,0 @@
/* =============================================================
* bootstrap-combobox.js v1.1.7
* =============================================================
* Copyright 2012 Daniel Farrell
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ============================================================ */
!function( $ ) {
"use strict";
/* COMBOBOX PUBLIC CLASS DEFINITION
* ================================ */
var Combobox = function ( element, options ) {
this.options = $.extend({}, $.fn.combobox.defaults, options);
this.template = this.options.template || this.template
this.$source = $(element);
this.$container = this.setup();
this.$element = this.$container.find('input[type=text]');
this.$target = this.$container.find('input[type=hidden]');
this.$button = this.$container.find('.dropdown-toggle');
this.$menu = $(this.options.menu).appendTo('body');
this.matcher = this.options.matcher || this.matcher;
this.sorter = this.options.sorter || this.sorter;
this.highlighter = this.options.highlighter || this.highlighter;
this.shown = false;
this.selected = false;
this.refresh();
this.transferAttributes();
this.listen();
};
Combobox.prototype = {
constructor: Combobox
, setup: function () {
var combobox = $(this.template());
this.$source.before(combobox);
this.$source.hide();
return combobox;
}
, disable: function() {
this.$element.prop('disabled', true);
this.$button.attr('disabled', true);
this.disabled = true;
this.$container.addClass('combobox-disabled');
}
, enable: function() {
this.$element.prop('disabled', false);
this.$button.attr('disabled', false);
this.disabled = false;
this.$container.removeClass('combobox-disabled');
}
, parse: function () {
var that = this
, map = {}
, source = []
, selected = false
, selectedValue = '';
this.$source.find('option').each(function() {
var option = $(this);
if (option.val() === '') {
that.options.placeholder = option.text();
return;
}
map[option.text()] = option.val();
source.push(option.text());
if (option.prop('selected')) {
selected = option.text();
selectedValue = option.val();
}
})
this.map = map;
if (selected) {
this.$element.val(selected);
this.$target.val(selectedValue);
this.$container.addClass('combobox-selected');
this.selected = true;
}
return source;
}
, transferAttributes: function() {
this.options.placeholder = this.$source.attr('data-placeholder') || this.options.placeholder
if(this.options.appendId !== "undefined") {
this.$element.attr('id', this.$source.attr('id') + this.options.appendId);
}
this.$element.attr('placeholder', this.options.placeholder)
this.$target.prop('name', this.$source.prop('name'))
this.$target.val(this.$source.val())
this.$source.removeAttr('name') // Remove from source otherwise form will pass parameter twice.
this.$element.attr('required', this.$source.attr('required'))
this.$element.attr('rel', this.$source.attr('rel'))
this.$element.attr('title', this.$source.attr('title'))
this.$element.attr('class', this.$source.attr('class'))
this.$element.attr('tabindex', this.$source.attr('tabindex'))
this.$source.removeAttr('tabindex')
if (this.$source.attr('disabled')!==undefined)
this.disable();
}
, select: function () {
var val = this.$menu.find('.active').attr('data-value');
this.$element.val(this.updater(val)).trigger('change');
this.$target.val(this.map[val]).trigger('change');
this.$source.val(this.map[val]).trigger('change');
this.$container.addClass('combobox-selected');
this.selected = true;
return this.hide();
}
, updater: function (item) {
return item;
}
, show: function () {
var pos = $.extend({}, this.$element.position(), {
height: this.$element[0].offsetHeight
});
this.$menu
.insertAfter(this.$element)
.css({
top: pos.top + pos.height
, left: pos.left
})
.show();
$('.dropdown-menu').on('mousedown', $.proxy(this.scrollSafety, this));
this.shown = true;
return this;
}
, hide: function () {
this.$menu.hide();
$('.dropdown-menu').off('mousedown', $.proxy(this.scrollSafety, this));
this.$element.on('blur', $.proxy(this.blur, this));
this.shown = false;
return this;
}
, lookup: function (event) {
this.query = this.$element.val();
return this.process(this.source);
}
, process: function (items) {
var that = this;
items = $.grep(items, function (item) {
return that.matcher(item);
})
items = this.sorter(items);
if (!items.length) {
return this.shown ? this.hide() : this;
}
return this.render(items.slice(0, this.options.items)).show();
}
, template: function() {
if (this.options.bsVersion == '2') {
return '<div class="combobox-container"><input type="hidden" /> <div class="input-append"> <input type="text" autocomplete="false" /> <span class="add-on dropdown-toggle" data-dropdown="dropdown"> <span class="caret"/> <i class="icon-remove"/> </span> </div> </div>'
} else {
return '<div class="combobox-container"> <input type="hidden" /> <div class="input-group"> <input type="text" autocomplete="false" /> <span class="input-group-addon dropdown-toggle" data-dropdown="dropdown"> <span class="caret" /> <span class="glyphicon glyphicon-remove" /> </span> </div> </div>'
}
}
, matcher: function (item) {
return ~item.toLowerCase().indexOf(this.query.toLowerCase());
}
, sorter: function (items) {
var beginswith = []
, caseSensitive = []
, caseInsensitive = []
, item;
while (item = items.shift()) {
if (!item.toLowerCase().indexOf(this.query.toLowerCase())) {beginswith.push(item);}
else if (~item.indexOf(this.query)) {caseSensitive.push(item);}
else {caseInsensitive.push(item);}
}
return beginswith.concat(caseSensitive, caseInsensitive);
}
, highlighter: function (item) {
var query = this.query.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&');
return item.replace(new RegExp('(' + query + ')', 'ig'), function ($1, match) {
return '<strong>' + match + '</strong>';
})
}
, render: function (items) {
var that = this;
items = $(items).map(function (i, item) {
i = $(that.options.item).attr('data-value', item);
i.find('a').html(that.highlighter(item));
return i[0];
})
items.first().addClass('active');
this.$menu.html(items);
return this;
}
, next: function (event) {
var active = this.$menu.find('.active').removeClass('active')
, next = active.next();
if (!next.length) {
next = $(this.$menu.find('li')[0]);
}
next.addClass('active');
}
, prev: function (event) {
var active = this.$menu.find('.active').removeClass('active')
, prev = active.prev();
if (!prev.length) {
prev = this.$menu.find('li').last();
}
prev.addClass('active');
}
, toggle: function () {
if (!this.disabled) {
if (this.$container.hasClass('combobox-selected')) {
this.clearTarget();
this.triggerChange();
this.clearElement();
} else {
if (this.shown) {
this.hide();
} else {
this.clearElement();
this.lookup();
}
}
}
}
, scrollSafety: function(e) {
if (e.target.tagName == 'UL') {
this.$element.off('blur');
}
}
, clearElement: function () {
this.$element.val('').focus();
}
, clearTarget: function () {
this.$source.val('');
this.$target.val('');
this.$container.removeClass('combobox-selected');
this.selected = false;
}
, triggerChange: function () {
this.$source.trigger('change');
}
, refresh: function () {
this.source = this.parse();
this.options.items = this.source.length;
}
, listen: function () {
this.$element
.on('focus', $.proxy(this.focus, this))
.on('blur', $.proxy(this.blur, this))
.on('keypress', $.proxy(this.keypress, this))
.on('keyup', $.proxy(this.keyup, this));
if (this.eventSupported('keydown')) {
this.$element.on('keydown', $.proxy(this.keydown, this));
}
this.$menu
.on('click', $.proxy(this.click, this))
.on('mouseenter', 'li', $.proxy(this.mouseenter, this))
.on('mouseleave', 'li', $.proxy(this.mouseleave, this));
this.$button
.on('click', $.proxy(this.toggle, this));
}
, eventSupported: function(eventName) {
var isSupported = eventName in this.$element;
if (!isSupported) {
this.$element.setAttribute(eventName, 'return;');
isSupported = typeof this.$element[eventName] === 'function';
}
return isSupported;
}
, move: function (e) {
if (!this.shown) {return;}
switch(e.keyCode) {
case 9: // tab
case 13: // enter
case 27: // escape
e.preventDefault();
break;
case 38: // up arrow
e.preventDefault();
this.prev();
this.fixMenuScroll();
break;
case 40: // down arrow
e.preventDefault();
this.next();
this.fixMenuScroll();
break;
}
e.stopPropagation();
}
, fixMenuScroll: function(){
var active = this.$menu.find('.active');
if(active.length){
var top = active.position().top;
var bottom = top + active.height();
var scrollTop = this.$menu.scrollTop();
var menuHeight = this.$menu.height();
if(bottom > menuHeight){
this.$menu.scrollTop(scrollTop + bottom - menuHeight);
} else if(top < 0){
this.$menu.scrollTop(scrollTop + top);
}
}
}
, keydown: function (e) {
this.suppressKeyPressRepeat = ~$.inArray(e.keyCode, [40,38,9,13,27]);
this.move(e);
}
, keypress: function (e) {
if (this.suppressKeyPressRepeat) {return;}
this.move(e);
}
, keyup: function (e) {
switch(e.keyCode) {
case 40: // down arrow
if (!this.shown){
this.toggle();
}
break;
case 39: // right arrow
case 38: // up arrow
case 37: // left arrow
case 36: // home
case 35: // end
case 16: // shift
case 17: // ctrl
case 18: // alt
break;
case 9: // tab
case 13: // enter
if (!this.shown) {return;}
this.select();
break;
case 27: // escape
if (!this.shown) {return;}
this.hide();
break;
default:
this.clearTarget();
this.lookup();
}
e.stopPropagation();
e.preventDefault();
}
, focus: function (e) {
this.focused = true;
}
, blur: function (e) {
var that = this;
this.focused = false;
var val = this.$element.val();
if (!this.selected && val !== '' ) {
this.$element.val('');
this.$source.val('').trigger('change');
this.$target.val('').trigger('change');
}
if (!this.mousedover && this.shown) {setTimeout(function () { that.hide(); }, 200);}
}
, click: function (e) {
e.stopPropagation();
e.preventDefault();
this.select();
this.$element.focus();
}
, mouseenter: function (e) {
this.mousedover = true;
this.$menu.find('.active').removeClass('active');
$(e.currentTarget).addClass('active');
}
, mouseleave: function (e) {
this.mousedover = false;
}
};
/* COMBOBOX PLUGIN DEFINITION
* =========================== */
$.fn.combobox = function ( option ) {
return this.each(function () {
var $this = $(this)
, data = $this.data('combobox')
, options = typeof option == 'object' && option;
if(!data) {$this.data('combobox', (data = new Combobox(this, options)));}
if (typeof option == 'string') {data[option]();}
});
};
$.fn.combobox.defaults = {
bsVersion: '3'
, menu: '<ul class="typeahead typeahead-long dropdown-menu"></ul>'
, item: '<li><a href="#"></a></li>'
};
$.fn.combobox.Constructor = Combobox;
}( window.jQuery );

View File

@ -1,237 +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 docutils import nodes
from docutils.parsers.rst.directives.tables import Table
from docutils.statemachine import ViewList
from six.moves.http_client import responses
import yaml
# cache for file -> yaml so we only do the load and check of a yaml
# file once during a sphinx processing run.
HTTP_YAML_CACHE = {}
class HTTPResponseCodeDirective(Table):
headers = ["Code", "Reason"]
status_types = ("success", "error")
# This is for HTTP response codes that OpenStack may use that are not part
# the httplib response dict.
CODES = {
429: "Too Many Requests",
}
required_arguments = 2
def __init__(self, *args, **kwargs):
self.CODES.update(responses)
super(HTTPResponseCodeDirective, self).__init__(*args, **kwargs)
def _load_status_file(self, fpath):
global HTTP_YAML_CACHE
if fpath in HTTP_YAML_CACHE:
return HTTP_YAML_CACHE[fpath]
# self.app.info("Fpath: %s" % fpath)
try:
with open(fpath, 'r') as stream:
lookup = yaml.safe_load(stream)
except IOError:
self.app.warn(
"Parameters file %s not found" % fpath,
(self.env.docname, None))
return
except yaml.YAMLError as exc:
self.app.warn(exc)
raise
HTTP_YAML_CACHE[fpath] = lookup
return lookup
def run(self):
self.env = self.state.document.settings.env
self.app = self.env.app
# Make sure we have some content, which should be yaml that
# defines some parameters.
if not self.content:
error = self.state_machine.reporter.error(
'No parameters defined',
nodes.literal_block(self.block_text, self.block_text),
line=self.lineno)
return [error]
if not len(self.arguments) >= 2:
error = self.state_machine.reporter.error(
'%s' % self.arguments,
nodes.literal_block(self.block_text, self.block_text),
line=self.lineno)
return [error]
_, status_defs_file = self.env.relfn2path(self.arguments.pop())
status_type = self.arguments.pop()
self.status_defs = self._load_status_file(status_defs_file)
# self.app.info("%s" % str(self.status_defs))
if status_type not in self.status_types:
error = self.state_machine.reporter.error(
'Type %s is not one of %s' % (status_type, self.status_types),
nodes.literal_block(self.block_text, self.block_text),
line=self.lineno)
return [error]
self.yaml = self._load_codes()
self.max_cols = len(self.headers)
# TODO(sdague): it would be good to dynamically set column
# widths (or basically make the colwidth thing go away
# entirely)
self.options['widths'] = [30, 70]
self.col_widths = self.get_column_widths(self.max_cols)
if isinstance(self.col_widths, tuple):
# In docutils 0.13.1, get_column_widths returns a (widths,
# colwidths) tuple, where widths is a string (i.e. 'auto').
# See https://sourceforge.net/p/docutils/patches/120/.
self.col_widths = self.col_widths[1]
# Actually convert the yaml
title, messages = self.make_title()
# self.app.info("Title %s, messages %s" % (title, messages))
table_node = self.build_table()
self.add_name(table_node)
title_block = nodes.title(
text=status_type.capitalize())
section = nodes.section(ids=title_block)
section += title_block
section += table_node
return [section] + messages
def _load_codes(self):
content = "\n".join(self.content)
parsed = yaml.safe_load(content)
new_content = list()
for item in parsed:
if isinstance(item, int):
new_content.append((item, self.status_defs[item]['default']))
else:
try:
for code, reason in item.items():
new_content.append(
(code, self.status_defs[code][reason])
)
except KeyError:
self.app.warn(
"Could not find %s for code %s" % (reason, code))
new_content.append(
(code, self.status_defs[code]['default']))
return new_content
def build_table(self):
table = nodes.table()
tgroup = nodes.tgroup(cols=len(self.headers))
table += tgroup
# TODO(sdague): it would be really nice to figure out how not
# to have this stanza, it kind of messes up all of the table
# formatting because it doesn't let tables just be the right
# size.
tgroup.extend(
nodes.colspec(colwidth=col_width, colname='c' + str(idx))
for idx, col_width in enumerate(self.col_widths)
)
thead = nodes.thead()
tgroup += thead
row_node = nodes.row()
thead += row_node
row_node.extend(nodes.entry(h, nodes.paragraph(text=h))
for h in self.headers)
tbody = nodes.tbody()
tgroup += tbody
rows, groups = self.collect_rows()
tbody.extend(rows)
table.extend(groups)
return table
def add_col(self, node):
entry = nodes.entry()
entry.append(node)
return entry
def add_desc_col(self, value):
entry = nodes.entry()
result = ViewList(value.split('\n'))
self.state.nested_parse(result, 0, entry)
return entry
def collect_rows(self):
rows = []
groups = []
try:
# self.app.info("Parsed content is: %s" % self.yaml)
for code, desc in self.yaml:
h_code = http_code()
h_code['code'] = code
h_code['title'] = self.CODES.get(code, 'Unknown')
trow = nodes.row()
trow += self.add_col(h_code)
trow += self.add_desc_col(desc)
rows.append(trow)
except AttributeError as exc:
# if 'key' in locals():
self.app.warn("Failure on key: %s, values: %s. %s" %
(code, desc, exc))
# else:
# rows.append(self.show_no_yaml_error())
return rows, groups
def http_code_html(self, node):
tmpl = "<code>%(code)s - %(title)s</code>"
self.body.append(tmpl % node)
raise nodes.SkipNode
class http_code(nodes.Part, nodes.Element):
"""Node for http_code stanza
Because we need to insert very specific HTML at the final stage of
processing, the http_code stanza needs a custom node type. This
lets us accumulate the relevant data into this node, during
parsing, but not turn it into known sphinx types (lists, tables,
sections).
Then, during the final build phase we transform directly to the
html that we want.
NOTE: this means we error trying to build latex or man pages for
these stanza types right now. This is all fixable if we add an
output formatter for this node type, but it's not yet a
priority. Contributions welcomed.
"""
pass

View File

@ -1,95 +0,0 @@
# Copyright 2010-2011 OpenStack Foundation
# 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 os
import fixtures
import testtools
def example_dir(name=""):
return os.path.join(os.path.dirname(__file__), 'examples', name)
_TRUE_VALUES = ('True', 'true', '1', 'yes')
class OutputStreamCapture(fixtures.Fixture):
"""Capture output streams during tests.
This fixture captures errant printing to stderr / stdout during
the tests and lets us see those streams at the end of the test
runs instead. Useful to see what was happening during failed
tests.
"""
def setUp(self):
super(OutputStreamCapture, self).setUp()
if os.environ.get('OS_STDOUT_CAPTURE') in _TRUE_VALUES:
self.out = self.useFixture(fixtures.StringStream('stdout'))
self.useFixture(
fixtures.MonkeyPatch('sys.stdout', self.out.stream))
if os.environ.get('OS_STDERR_CAPTURE') in _TRUE_VALUES:
self.err = self.useFixture(fixtures.StringStream('stderr'))
self.useFixture(
fixtures.MonkeyPatch('sys.stderr', self.err.stream))
@property
def stderr(self):
return self.err._details["stderr"].as_text()
@property
def stdout(self):
return self.out._details["stdout"].as_text()
class Timeout(fixtures.Fixture):
"""Setup per test timeouts.
In order to avoid test deadlocks we support setting up a test
timeout parameter read from the environment. In almost all
cases where the timeout is reached this means a deadlock.
A class level TIMEOUT_SCALING_FACTOR also exists, which allows
extremely long tests to specify they need more time.
"""
def __init__(self, timeout, scaling=1):
super(Timeout, self).__init__()
try:
self.test_timeout = int(timeout)
except ValueError:
# If timeout value is invalid do not set a timeout.
self.test_timeout = 0
if scaling >= 1:
self.test_timeout *= scaling
else:
raise ValueError('scaling value must be >= 1')
def setUp(self):
super(Timeout, self).setUp()
if self.test_timeout > 0:
self.useFixture(fixtures.Timeout(self.test_timeout, gentle=True))
class TestCase(testtools.TestCase):
"""Test case base class for all unit tests."""
def setUp(self):
"""Run before each test method to initialize test environment."""
super(TestCase, self).setUp()
self.useFixture(Timeout(
os.environ.get('OS_TEST_TIMEOUT', 0)))
self.useFixture(OutputStreamCapture())

View File

@ -1,35 +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.
# -- 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.
import openstackdocstheme
html_theme = 'openstackdocs'
html_theme_path = [openstackdocstheme.get_html_theme_path()]
html_theme_options = {
"sidebar_mode": "toc",
}
extensions = [
'os_api_ref',
]
# The suffix of source filenames.
source_suffix = '.rst'
# The master toctree document.
master_doc = 'index'

View File

@ -1,32 +0,0 @@
.. rest_expand_all::
I am text, hear me roar!
==============
List Servers
==============
.. rest_method:: GET /servers
.. rest_parameters:: parameters.yaml
- name: name
Response codes
--------------
.. rest_status_code:: success status.yaml
- 200
- 100
- 201
.. rest_status_code:: error status.yaml
- 405
- 403
- 401
- 400
- 500
- 409: duplcate_zone

View File

@ -1,6 +0,0 @@
name:
in: body
required: true
type: string
description: |
The name of things

View File

@ -1,39 +0,0 @@
#################
# Success Codes #
#################
100:
default: |
An unusual code for an API
200:
default: |
Request was successful.
201:
default: |
Resource was created and is ready to use.
#################
# Error Codes #
#################
400:
default: |
Some content in the request was invalid
zone_data_error: |
Some of the data for the
401:
default: |
User must authenticate before making a request
403:
default: |
Policy does not allow current user to do this operation.
405:
default: |
Method is not valid for this endpoint.
409:
default: |
This operation conflicted with another operation on this resource
duplcate_zone: |
There is already a zone with this name.
500:
default: |
Something went wrong inside the service.

View File

@ -1,38 +0,0 @@
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
# -- 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.
import openstackdocstheme
html_theme = 'openstackdocs'
html_theme_path = [openstackdocstheme.get_html_theme_path()]
html_theme_options = {
"sidebar_mode": "toc",
}
extensions = [
'os_api_ref',
]
# The suffix of source filenames.
source_suffix = '.rst'
# The master toctree document.
master_doc = 'index'
os_api_ref_min_microversion = '2.1'
os_api_ref_max_microversion = '2.30'

View File

@ -1,23 +0,0 @@
.. rest_expand_all::
I am text, hear me roar!
==============
List Servers
==============
.. rest_method:: GET /servers
.. rest_parameters:: parameters.yaml
- name: name
- name2: name2
- name3: name3
===========
List Tags
===========
.. rest_method:: GET /tags
min_version: 2.17
max_version: 2.19

View File

@ -1,20 +0,0 @@
name:
in: body
required: true
type: string
description: |
The name of things
name2:
in: body
required: true
type: string
description: |
The name of things
min_version: 2.11
name3:
in: body
required: true
type: string
description: |
The name of things
max_version: 2.20

View File

@ -1,35 +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.
# -- 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.
import openstackdocstheme
html_theme = 'openstackdocs'
html_theme_path = [openstackdocstheme.get_html_theme_path()]
html_theme_options = {
"sidebar_mode": "toc",
}
extensions = [
'os_api_ref',
]
# The suffix of source filenames.
source_suffix = '.rst'
# The master toctree document.
master_doc = 'index'

View File

@ -1 +0,0 @@
# Empty parameter file

View File

@ -1,55 +0,0 @@
.. rest_expand_all::
I am text, hear me roar!
==============
List Servers
==============
.. rest_method:: GET /servers
.. rest_parameters:: parameters.yaml
- name: name
- name: lookup_key_name
- name: name_1
- invalid_name
No Parameters Specified
-----------------------
.. rest_parameters:: parameters.yaml
Empty File and Parameters Specified
-----------------------------------
.. rest_parameters:: empty_parameters_file.yaml
- name: name
Nonexistent Parameter File
--------------------------
.. rest_parameters:: no_parameters.yaml
Check missing path parameters in stanza
---------------------------------------
.. rest_method:: GET /server/{server_id}/{new_id}/{new_id2}
.. rest_parameters:: parameters.yaml
- server_id: server_id
Check another missing path parameters in stanza
-----------------------------------------------
.. rest_method:: GET /server/{b_id}/{c_id2}/{server_id}
.. rest_parameters:: parameters.yaml
- server_id: server_id

View File

@ -1,25 +0,0 @@
# valid path parameter
server_id:
description: |
ID for server.
in: path
required: true
type: string
# These are out of order, this should be a warning.
name2:
in: body
required: false
type: string
description: |
foo
name:
in: body
required: true
type: string
description: |
The name of things
name_1:
description: |
name_1 is missing type field.
in: body
required: true

View File

@ -1,163 +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.
"""
test_os_api_ref
----------------------------------
Tests for `os_api_ref` module.
"""
from bs4 import BeautifulSoup
from sphinx_testing import with_app
from os_api_ref.tests import base
class TestBasicExample(base.TestCase):
"""Test basic rendering.
This can be used to test that basic rendering works for these
examples, so if someone breaks something we know.
"""
@with_app(buildername='html', srcdir=base.example_dir('basic'),
copy_srcdir_to_tmpdir=True)
def setUp(self, app, status, warning):
super(TestBasicExample, self).setUp()
self.app = app
self.status = status
self.warning = warning
self.app.build()
self.html = (app.outdir / 'index.html').read_text()
self.soup = BeautifulSoup(self.html, 'html.parser')
self.content = str(self.soup)
def test_expand_all(self):
"""Do we get an expand all button like we expect."""
content = str(self.soup.find(id='expand-all'))
example_button = ('<button class="btn btn-info btn-sm btn-expand-all" '
'data-toggle="collapse" id="expand-all">'
'Show All</button>')
self.assertEqual(
example_button,
content)
def test_rest_method(self):
"""Do we get a REST method call block"""
# TODO(sdague): it probably would make sense to do this as a
# whole template instead of parts.
content = str(self.soup.find_all(class_='operation-grp'))
self.assertIn(
'<span class="glyphicon glyphicon-link"></span>',
str(content))
self.assertIn(
'<span class="label label-GET">GET</span>',
str(content))
self.assertIn(
'<div class="row col-md-12">/servers</div>',
str(content))
self.assertIn(
('<button class="btn btn-info btn-sm btn-detail" '
'data-target="#list-servers-detail" data-toggle="collapse" '
'id="list-servers-detail-btn">detail</button>'),
str(content))
def test_parameters(self):
"""Do we get some parameters table"""
table = """<table border="1" class="docutils">
<colgroup>
<col width="20%"/>
<col width="10%"/>
<col width="10%"/>
<col width="60%"/>
</colgroup>
<thead valign="bottom">
<tr class="row-odd"><th class="head">Name</th>
<th class="head">In</th>
<th class="head">Type</th>
<th class="head">Description</th>
</tr>
</thead>
<tbody valign="top">
<tr class="row-even"><td>name</td>
<td>body</td>
<td>string</td>
<td>The name of things</td>
</tr>
</tbody>
</table>"""
self.assertIn(table, self.content)
def test_rest_response(self):
success_table = """table border="1" class="docutils">
<colgroup>
<col width="30%"/>
<col width="70%"/>
</colgroup>
<thead valign="bottom">
<tr class="row-odd"><th class="head">Code</th>
<th class="head">Reason</th>
</tr>
</thead>
<tbody valign="top">
<tr class="row-even"><td><code>200 - OK</code></td>
<td>Request was successful.</td>
</tr>
<tr class="row-odd"><td><code>100 - Continue</code></td>
<td>An unusual code for an API</td>
</tr>
<tr class="row-even"><td><code>201 - Created</code></td>
<td>Resource was created and is ready to use.</td>
</tr>
</tbody>
</table>
"""
error_table = """<table border="1" class="docutils">
<colgroup>
<col width="30%"/>
<col width="70%"/>
</colgroup>
<thead valign="bottom">
<tr class="row-odd"><th class="head">Code</th>
<th class="head">Reason</th>
</tr>
</thead>
<tbody valign="top">
<tr class="row-even"><td><code>405 - Method Not Allowed</code></td>
<td>Method is not valid for this endpoint.</td>
</tr>
<tr class="row-odd"><td><code>403 - Forbidden</code></td>
<td>Policy does not allow current user to do this operation.</td>
</tr>
<tr class="row-even"><td><code>401 - Unauthorized</code></td>
<td>User must authenticate before making a request</td>
</tr>
<tr class="row-odd"><td><code>400 - Bad Request</code></td>
<td>Some content in the request was invalid</td>
</tr>
<tr class="row-even"><td><code>500 - Internal Server Error</code></td>
<td>Something went wrong inside the service.</td>
</tr>
<tr class="row-odd"><td><code>409 - Conflict</code></td>
<td>There is already a zone with this name.</td>
</tr>
</tbody>
</table>
"""
self.assertIn(success_table, self.content)
self.assertIn(error_table, self.content)

View File

@ -1,106 +0,0 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
test_os_api_ref
----------------------------------
Tests for `os_api_ref` module.
"""
from bs4 import BeautifulSoup
from sphinx_testing import with_app
from os_api_ref.tests import base
class TestMicroversions(base.TestCase):
"""Test basic rendering.
This can be used to test that basic rendering works for these
examples, so if someone breaks something we know.
"""
@with_app(buildername='html', srcdir=base.example_dir('microversions'),
copy_srcdir_to_tmpdir=True)
def setUp(self, app, status, warning):
super(TestMicroversions, self).setUp()
self.app = app
self.app.build()
self.status = status.getvalue()
self.warning = warning.getvalue()
self.html = (app.outdir / 'index.html').read_text()
self.soup = BeautifulSoup(self.html, 'html.parser')
self.content = str(self.soup)
def test_rest_method(self):
"""Test that min / max mv css class attributes are set"""
content = self.soup.find_all(class_='rp_min_ver_2_17')
self.assertIn(
'<div class="operation-grp rp_min_ver_2_17 rp_max_ver_2_19 ">',
str(content[0]))
content = self.soup.find_all(class_='rp_max_ver_2_19')
self.assertIn(
'<div class="operation-grp rp_min_ver_2_17 rp_max_ver_2_19 ">',
str(content[0]))
def test_parameters_table(self):
"""Test that min / max mv css class attributes are set in params"""
table = """<div class="api-detail collapse section" id="list-servers-detail">
<table border="1" class="docutils">
<colgroup>
<col width="20%"/>
<col width="10%"/>
<col width="10%"/>
<col width="60%"/>
</colgroup>
<thead valign="bottom">
<tr class="row-odd"><th class="head">Name</th>
<th class="head">In</th>
<th class="head">Type</th>
<th class="head">Description</th>
</tr>
</thead>
<tbody valign="top">
<tr class="row-even"><td>name</td>
<td>body</td>
<td>string</td>
<td>The name of things</td>
</tr>
<tr class="rp_min_ver_2_11 row-odd"><td>name2</td>
<td>body</td>
<td>string</td>
<td><p class="first">The name of things</p>
<p class="last"><strong>New in version 2.11</strong></p>
</td>
</tr>
<tr class="rp_max_ver_2_20 row-even"><td>name3</td>
<td>body</td>
<td>string</td>
<td><p class="first">The name of things</p>
<p class="last"><strong>Deprecated in version 2.20</strong></p>
</td>
</tr>
</tbody>
</table>
</div>
""" # noqa
self.assertIn(table, self.content)
def test_mv_selector(self):
button_selectors = '<option selected="selected" value="">All</option><option value="2.1">2.1</option><option value="2.2">2.2</option><option value="2.3">2.3</option><option value="2.4">2.4</option><option value="2.5">2.5</option><option value="2.6">2.6</option><option value="2.7">2.7</option><option value="2.8">2.8</option><option value="2.9">2.9</option><option value="2.10">2.10</option><option value="2.11">2.11</option><option value="2.12">2.12</option><option value="2.13">2.13</option><option value="2.14">2.14</option><option value="2.15">2.15</option><option value="2.16">2.16</option><option value="2.17">2.17</option><option value="2.18">2.18</option><option value="2.19">2.19</option><option value="2.20">2.20</option><option value="2.21">2.21</option><option value="2.22">2.22</option><option value="2.23">2.23</option><option value="2.24">2.24</option><option value="2.25">2.25</option><option value="2.26">2.26</option><option value="2.27">2.27</option><option value="2.28">2.28</option><option value="2.29">2.29</option><option value="2.30">2.30</option>' # noqa
self.assertIn(button_selectors, self.content)
def test_js_declares(self):
self.assertIn("os_max_mv = 30;", self.content)
self.assertIn("os_min_mv = 1;", self.content)

View File

@ -1,26 +0,0 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
test_os_api_ref
----------------------------------
Tests for `os_api_ref` module.
"""
from os_api_ref.tests import base
class TestOs_api_ref(base.TestCase):
def test_something(self):
pass

View File

@ -1,101 +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.
"""
test_os_api_ref
----------------------------------
Tests for `os_api_ref` module.
"""
from bs4 import BeautifulSoup
from sphinx_testing import with_app
from os_api_ref.tests import base
class TestWarnings(base.TestCase):
"""Test basic rendering.
This can be used to test that basic rendering works for these
examples, so if someone breaks something we know.
"""
@with_app(buildername='html', srcdir=base.example_dir('warnings'),
copy_srcdir_to_tmpdir=True)
def setUp(self, app, status, warning):
super(TestWarnings, self).setUp()
self.app = app
self.app.build()
self.status = status.getvalue()
self.warning = warning.getvalue()
self.html = (app.outdir / 'index.html').read_text()
self.soup = BeautifulSoup(self.html, 'html.parser')
self.content = str(self.soup)
def test_out_of_order(self):
"""Do we get an out of order naming warning."""
self.assertIn(
("WARNING: Parameters out of order ``name2`` "
"should be after ``name``"),
self.warning)
def test_missing_lookup_name(self):
"""Warning when missing a lookup key in parameter file."""
self.assertIn(
("WARNING: No field definition for ``lookup_key_name`` found in "),
self.warning)
def test_missing_field(self):
"""Warning when missing type field in parameter file."""
self.assertIn(
("WARNING: Failure on key: name, values: "
+ "OrderedDict([('description',"
+ " 'name_1 is missing type field.\\n'), ('in', 'body'),"
+ " ('required', True)]). "
+ "'NoneType' object has no attribute 'split'"),
self.warning)
def test_invalid_parameter_definition(self):
"""Warning when parameter definition is invalid."""
self.assertIn(
("WARNING: Invalid parameter definition ``invalid_name``. "
+ "Expected format: ``name: reference``. "),
self.warning)
def test_empty_parameter_file(self):
"""Warning when parameter file exists but is empty."""
self.assertIn(
("WARNING: Parameters file is empty"),
self.warning)
def test_no_parameters_set(self):
"""Error when parameters are not set in rest_parameters stanza."""
self.assertIn(
("No parameters defined\n\n.."
+ " rest_parameters:: parameters.yaml"),
self.warning)
def test_parameter_file_not_exist(self):
"""Error when parameter file does not exist"""
self.assertIn(
("No parameters defined\n\n.."
+ " rest_parameters:: no_parameters.yaml"),
self.warning)
def test_missing_path_parameter_in_stanza(self):
"""Warning when path param not found in rest_parameter stanza."""
self.assertIn(
("WARNING: No path parameter ``b_id`` found in"
+ " rest_parameter stanza.\n"),
self.warning)

View File

@ -1,9 +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.
pbr!=2.1.0,>=2.0.0 # Apache-2.0
PyYAML>=3.10.0 # MIT
six>=1.9.0 # MIT
sphinx!=1.6.1,>=1.5.1 # BSD
openstackdocstheme>=1.5.0 # Apache-2.0

View File

@ -1,46 +0,0 @@
[metadata]
name = os-api-ref
summary = Sphinx Extensions to support API reference sites in OpenStack
description-file =
README.rst
author = OpenStack
author-email = openstack-dev@lists.openstack.org
home-page = http://docs.openstack.org/developer/os-api-ref
classifier =
Environment :: OpenStack
Intended Audience :: Information Technology
Intended Audience :: System Administrators
License :: OSI Approved :: Apache Software License
Operating System :: POSIX :: Linux
Programming Language :: Python
Programming Language :: Python :: 2
Programming Language :: Python :: 2.7
Programming Language :: Python :: 3
Programming Language :: Python :: 3.4
[files]
packages =
os_api_ref
[build_sphinx]
source-dir = doc/source
build-dir = doc/build
all_files = 1
warning-is-error = 1
[upload_sphinx]
upload-dir = doc/build/html
[compile_catalog]
directory = os_api_ref/locale
domain = os_api_ref
[update_catalog]
domain = os_api_ref
output_dir = os_api_ref/locale
input_file = os_api_ref/locale/os_api_ref.pot
[extract_messages]
keywords = _ gettext ngettext l_ lazy_gettext
mapping_file = babel.cfg
output_file = os_api_ref/locale/os_api_ref.pot

View File

@ -1,29 +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.
# THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT
import setuptools
# In python < 2.7.4, a lazy loading of package `pbr` will break
# setuptools if some other modules registered functions in `atexit`.
# solution from: http://bugs.python.org/issue15881#msg170215
try:
import multiprocessing # noqa
except ImportError:
pass
setuptools.setup(
setup_requires=['pbr>=2.0.0'],
pbr=True)

View File

@ -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.
hacking!=0.13.0,<0.14,>=0.12.0 # Apache-2.0
coverage!=4.4,>=4.0 # Apache-2.0
python-subunit>=0.0.18 # Apache-2.0/BSD
oslosphinx>=4.7.0 # Apache-2.0
testrepository>=0.0.18 # Apache-2.0/BSD
testtools>=1.4.0 # MIT
sphinx-testing # BSD License
beautifulsoup4 # MIT

View File

@ -1,30 +0,0 @@
#!/usr/bin/env bash
# Client constraint file contains this client version pin that is in conflict
# with installing the client from source. We should remove the version pin in
# the constraints file before applying it for from-source installation.
CONSTRAINTS_FILE="$1"
shift 1
set -e
# NOTE(tonyb): Place this in the tox enviroment's log dir so it will get
# published to logs.openstack.org for easy debugging.
localfile="$VIRTUAL_ENV/log/upper-constraints.txt"
if [[ "$CONSTRAINTS_FILE" != http* ]]; then
CONSTRAINTS_FILE="file://$CONSTRAINTS_FILE"
fi
# NOTE(tonyb): need to add curl to bindep.txt if the project supports bindep
curl "$CONSTRAINTS_FILE" --insecure --progress-bar --output "$localfile"
pip install -c"$localfile" openstack-requirements
# This is the main purpose of the script: Allow local installation of
# the current repo. It is listed in constraints file and thus any
# install will be constrained and we need to unconstrain it.
edit-constraints "$localfile" -- "$CLIENT_NAME"
pip install -c"$localfile" -U "$@"
exit $?

38
tox.ini
View File

@ -1,38 +0,0 @@
[tox]
minversion = 2.0
envlist = py27,pep8,docs
skipsdist = True
[testenv]
setenv =
VIRTUAL_ENV={envdir}
BRANCH_NAME=master
CLIENT_NAME=os-api-ref
install_command = {toxinidir}/tools/tox_install.sh {env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} {opts} {packages}
pip install -U {opts} {packages}
deps = -r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt
commands = python setup.py test --slowest --no-parallel --testr-args='{posargs}'
[testenv:pep8]
commands = flake8 {posargs}
[testenv:venv]
commands = {posargs}
[testenv:cover]
commands = python setup.py test --coverage --testr-args='{posargs}'
[testenv:docs]
commands = python setup.py build_sphinx
[testenv:debug]
commands = oslo_debug_helper {posargs}
[flake8]
# E123, E125 skipped as they are invalid PEP-8.
show-source = True
ignore = E123,E125,E129
builtins = _
exclude=.venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,build