codegenerator/doc/source/openapi.rst
gtema 4d23d8aab5 Initial import
Depends-On: https://review.opendev.org/c/openstack/governance/+/910581

Change-Id: I87866224d9ac89d3da150c95a86a20afb48dc4d2
2024-03-06 10:07:54 +00:00

187 lines
7.6 KiB
ReStructuredText

OpenApi Schema
==============
CodeGenerator is able to generate OpenAPI specs for certain services by
inspecting their code. This requires service package being installed in the
environment where the generator is running. It then tries to initialize service
application and for supported runtimes scans for the exposed operations. At the
moment following services are covered:
- Nova
- Neutron
- Cinder
- Glance
- Keystone
- Octavia
Generator can be invoked after installing it as a regular python project with
dependencies
.. code-block:: console
openstack-codegenerator --target openapi-spec --work-dir wrk --service-type compute
The generator is having possibility to additionally parse rendered service
API-REF HTML documentation and supplement descriptions in the generated
OpenApi spec by trying to find corresponding information in the html.
.. code-block:: console
openstack-codegenerator --target openapi-spec --work-dir wrk --service-type compute --api-ref-src <PATH_TO_RENDERED_DOC>.html
Another project for rendering generated OpenAPI specs in the style
similar (but not the same way) to currently used os-api-ref:
`https://github.com/gtema/openstack-openapi`. It implements a
Sphinx extension that reads spec file and converts it to internal
sphinx directives to get a proper structure in the rendered HTML
and styles it using `BootstrapV5` library. Sample rendering can be
seen under `https://gtema.github.io/openstack-openapi/`
Highlevel description (for contributor)
---------------------------------------
Base generator
:class:`~codegenerator.openapi.base.OpenStackServerSourceGenerator` is
supporting WSGI + Routes based application out of box. For such applications
it tries to get the main router from wich all exposed routes are being
analysed. During routes processing generator is searching for supported
decorators and frameworks in order to extract as most information about the
operation as possible:
- url
- method
- path parameters
- query parameters
- expected body jsonschema
- response jsonschema
- expected response and error codes
- operation description (docstrings)
Generator for every covered OpenStack service is inherits from the Base
generator (i.e. :class:`~codegenerator.openapi.nova.NovaGenerator`. It is
expected that `init` method will perform service setup activities (i.e.
database creation or config file preparation whenever required) and sets the
main application router. `generate` method of the class is then being invoked
and it reads current spec file (if present to update it) and loops over all
exposed routes. For each route a dedicated method `_process_route` is
invoked, which in turn invoke multiple additional methods for parameters or
body schemas processing.
After processing when api-ref html is available a dedicated method
:class:`~codegenerator.openapi.utils.merge_api_ref_doc` can be called to add
available descriptions (operation, parameters).
.. note::
Since all services use `oslo_config` and `oslo_policy` libraries which rely
on global state they race with each other. In order to avoid this processing
rely on multiprocessing to isolate services.
Nova
----
Source code of Nova currently provides full information about exposed routes
and query/path parameters, as well as jsonschema of request body. Sadly it does
not contain jsonschemas of the responses. CodeGenerator at the moment covers
those missing schemas directly in the code and injects them into the schema via
:class:`~codegenerator.openapi.nova.NovaGenerator:_get_schema_ref`
After stabilization it is expected to move implemented schemas into the Nova
source code base.
Cinder
-------
Cinder is very similar to Nova so everything mentioned above is applicable
here as well.
for Cinder at the moment all operations are duplicated under
`v3/${project_id}/...` and `v3/...`. For the sake of standartization
project_id urls are excluded from the produces spec file.
Glance
------
Glance is also using `routes` for exposing application. However in difference
to Nova and Cinder it does not describe request parameters of bodies in an
expected way. Current implementation of the Glance generator therefore is
looking at the request serializer and deserializer attached to the operation
controllers. When this information is present and contain usable jsonschema
it is being used. In other cases similar approach to Nova with hardcoding
response information is being used. But since Glance code base contain
certain useful jsonschemas (not connected in the routes) generator gets those
schemas directly from the code (where the mapping is known).
Keystone
--------
This service is using `Flask` framework which gives similar capabilities to
the `routes`. However here there are no body information at all (neither
Request nor Response). Also here there are certain jsonschemas found directly
in the Keystone code base and connected for the schema generation.
Neutron
-------
This is where things are getting more challenging.
Neutron requires having DB provisioned and an in-memory DB seems not to be
possible due to technics for the DB communication. In addition to that config
file enabling desired extensions is expected. All this activities are covered
in :class:`~codegenrator.openapi.neutron.NeutronGenerator:setup_neutron`.
According to the current information it is not possible to have all possible
Neutron extensions and plugins enabled at the same time. This is solved by
generator spinning multiple subprocesses that bootstrap Neutron with different
configuration and then merge results. This is handled by spinning up Neutron
few times with independent configurations and merging resulting spec.
Additional challenge in Neutron is that it does not use `routes` to expose
operations directly, but is having a mix of `routes` based operations for
extensions and `pecan` app for the base functionality. Since the `pecan`
framework is based on a purely dynamic routing there is no possibility to
extract information about exposed routes by doing code inspection. Luckily only
base operations (router/net/subnet) are implemented this way. Therefore
generator registers known `pecan` operations into the extensions router and
normal generator flow is being invoked.
Next challenge is that for Neutron there is no description of bodies at all,
but certain controllers are having `API_DEFINITION` attached. While this is not
a jsonschema at all it can be used to create one where possible. Sadly there is
still sometime no possibility to properly estimate whether certain operation is
exposed and functioning or it is exposed but fails permanently due to the fact,
that `API_DEFINITION` extrapolation fails for this operation.
:class:`~codegenerator.openapi.neutron.get_schema` method is responsible for
conversion of the `API_DEFINITION` into the jsonschema, but is not able to work
perfectly until additional work is invested.
Certain additional operations (addRouterInterface, addExtraRoute, ...) are not
having any information available and require to be also hardcodede in the
generator.
Octavia
-------
Octavia is also based on the `pecan` with its dynamic routing, but the
majority of controllers are available for scanning due to the source code
classes hierarchy. To keep the generation process close to generics
:class:`~codegenerator.openapi.octavia.OctaviaGenerator` is constructing
`routes` router from this information and adds few known exceptions. For the
produced routing table generic process is being invoked which is then looking
at the `WSME` decorators attached to the exposed operations. Since `WSME`
schema is not a jsonschema on its own but it can be considered as an
alternative to jsonschema a naive conversion is implemented in
:class:`~codegenerator.openapi.base._convert_wsme_to_jsonschema`.