Merge "doc: Populate the 'contributor' section"
This commit is contained in:
commit
d4b9b0b19c
doc/source
376
doc/source/contributor/placement.rst
Normal file
376
doc/source/contributor/placement.rst
Normal file
@ -0,0 +1,376 @@
|
||||
..
|
||||
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.
|
||||
|
||||
===============================
|
||||
Placement API Developer Notes
|
||||
===============================
|
||||
|
||||
Overview
|
||||
========
|
||||
|
||||
The Nova project introduced the :doc:`placement service </placement>` as part
|
||||
of the Newton release. The service provides an HTTP API to manage inventories
|
||||
of different classes of resources, such as disk or virtual cpus, made available
|
||||
by entities called resource providers. Information provided through the
|
||||
placement API is intended to enable more effective accounting of resources in
|
||||
an OpenStack deployment and better scheduling of various entities in the cloud.
|
||||
|
||||
The document serves to explain the architecture of the system and to provide
|
||||
some guidance on how to maintain and extend the code. For more detail on why
|
||||
the system was created and how it does its job see :doc:`/placement`.
|
||||
|
||||
Big Picture
|
||||
===========
|
||||
|
||||
The placement service is straightforward: It is a `WSGI`_ application that
|
||||
sends and receives JSON, using an RDBMS (usually MySQL) for persistence.
|
||||
As state is managed solely in the DB, scaling the placement service is done by
|
||||
increasing the number of WSGI application instances and scaling the RDBMS using
|
||||
traditional database scaling techniques.
|
||||
|
||||
For sake of consistency and because there was initially intent to make the
|
||||
entities in the placement service available over RPC, `versioned objects`_ are
|
||||
used to provide the interface between the HTTP application layer and the
|
||||
SQLAlchemy-driven persistence layer. Even without RPC, these objects provide
|
||||
useful structuring and separation of the code.
|
||||
|
||||
Though the placement service doesn't aspire to be a `microservice` it does
|
||||
aspire to continue to be small and minimally complex. This means a relatively
|
||||
small amount of middleware that is not configurable, and a limited number of
|
||||
exposed resources where any given resource is represented by one (and only
|
||||
one) URL that expresses a noun that is a member of the system. Adding
|
||||
additional resources should be considered a significant change requiring robust
|
||||
review from many stakeholders.
|
||||
|
||||
The set of HTTP resources represents a concise and constrained grammar for
|
||||
expressing the management of resource providers, inventories, resource classes
|
||||
and allocations. If a solution is initially designed to need more resources or
|
||||
a more complex grammar that may be a sign that we need to give our goals
|
||||
greater scrutiny. Is there a way to do what we want with what we have already?
|
||||
Can some other service help? Is a new collaborating service required?
|
||||
|
||||
Minimal Framework
|
||||
=================
|
||||
|
||||
The API is set up to use a minimal framework that tries to keep the structure
|
||||
of the application as discoverable as possible and keeps the HTTP interaction
|
||||
near the surface. The goal of this is to make things easy to trace when
|
||||
debugging or adding functionality.
|
||||
|
||||
Functionality which is required for every request is handled in raw WSGI
|
||||
middleware that is composed in the `nova.api.openstack.placement.deploy`
|
||||
module. Dispatch or routing is handled declaratively via the
|
||||
``ROUTE_DECLARATIONS`` map defined in the
|
||||
`nova.api.openstack.placement.handler` module.
|
||||
|
||||
Mapping is by URL plus request method. The destination is a complete WSGI
|
||||
application, using a subclass of the `wsgify`_ method from `WebOb`_ to provide
|
||||
a `Request`_ object that provides convenience methods for accessing request
|
||||
headers, bodies, and query parameters and for generating responses. In the
|
||||
placement API these mini-applications are called `handlers`. The `wsgify`
|
||||
subclass is provided in `nova.api.openstack.placement.wsgi_wrapper` as
|
||||
`PlacementWsgify`. It is used to make sure that JSON formatted error responses
|
||||
are structured according to the API-WG `errors`_ guideline.
|
||||
|
||||
This division between middleware, dispatch and handlers is supposed to
|
||||
provide clues on where a particular behavior or functionality should be
|
||||
implemented. Like most such systems, this doesn't always work but is a useful
|
||||
tool.
|
||||
|
||||
Gotchas
|
||||
=======
|
||||
|
||||
This section tries to shed some light on some of the differences between the
|
||||
placement API and some of the nova APIs or on situations which may be
|
||||
surprising or unexpected.
|
||||
|
||||
* The placement API is somewhat more strict about `Content-Type` and `Accept`
|
||||
headers in an effort to follow the HTTP RFCs.
|
||||
|
||||
If a user-agent sends some JSON in a `PUT` or `POST` request without a
|
||||
`Content-Type` of `application/json` the request will result in an error.
|
||||
|
||||
If a `GET` request is made without an `Accept` header, the response will
|
||||
default to being `application/json`.
|
||||
|
||||
If a request is made with an explicit `Accept` header that does not include
|
||||
`application/json` then there will be an error and the error will attempt to
|
||||
be in the requested format (for example, `text/plain`).
|
||||
|
||||
* If a URL exists, but a request is made using a method that that URL does not
|
||||
support, the API will respond with a `405` error. Sometimes in the nova APIs
|
||||
this can be a `404` (which is wrong, but understandable given the constraints
|
||||
of the code).
|
||||
|
||||
* Because each handler is individually wrapped by the `PlacementWsgify`
|
||||
decorator any exception that is a subclass of `webob.exc.WSGIHTTPException`
|
||||
that is raised from within the handler, such as `webob.exc.HTTPBadRequest`,
|
||||
will be caught by WebOb and turned into a valid `Response`_ containing
|
||||
headers and body set by WebOb based on the information given when the
|
||||
exception was raised. It will not be seen as an exception by any of the
|
||||
middleware in the placement stack.
|
||||
|
||||
In general this is a good thing, but it can lead to some confusion if, for
|
||||
example, you are trying to add some middleware that operates on exceptions.
|
||||
|
||||
Other exceptions that are not from `WebOb`_ will raise outside the handlers
|
||||
where they will either be caught in the `__call__` method of the
|
||||
`PlacementHandler` app that is responsible for dispatch, or by the
|
||||
`FaultWrap` middleware.
|
||||
|
||||
Microversions
|
||||
=============
|
||||
|
||||
The placement API makes use of `microversions`_ to allow the release of new
|
||||
features on an opt in basis. See :doc:`/placement` for an up to date history of
|
||||
the available microversions.
|
||||
|
||||
The rules around when a microversion is needed are the same as for the
|
||||
:doc:`compute API </contributor/microversions>`. When adding a new microversion
|
||||
there are a few bits of required housekeeping that must be done in the code:
|
||||
|
||||
* Update the ``VERSIONS`` list in
|
||||
`nova.api.openstack.placement.microversion` to indicate the new
|
||||
microversion and give a very brief summary of the added feature.
|
||||
* Update `nova/api/openstack/placement/rest_api_version_history.rst`
|
||||
to add a more detailed section describing the new microversion.
|
||||
* Add a `release note`_ announcing the new or changed feature and
|
||||
the microversion.
|
||||
* If the ``version_handler`` decorator (see below) has been used,
|
||||
increment ``TOTAL_VERSIONED_METHODS`` in
|
||||
`nova/tests/unit/api/openstack/placement/test_microversion.py`.
|
||||
This provides a confirmatory check just to make sure you're paying
|
||||
attention and as a helpful reminder to do the other things in this
|
||||
list.
|
||||
|
||||
In the placement API, microversions only use the modern form of the
|
||||
version header::
|
||||
|
||||
OpenStack-API-Version: placement 1.2
|
||||
|
||||
If a valid microversion is present in a request it will be placed,
|
||||
as a ``Version`` object, into the WSGI environment with the
|
||||
``placement.microversion`` key. Often, accessing this in handler
|
||||
code directly (to control branching) is the most explicit and
|
||||
granular way to have different behavior per microversion. A
|
||||
``Version`` instance can be treated as a tuple of two ints and
|
||||
compared as such or there is a ``matches`` method.
|
||||
|
||||
In other cases there are some helper methods in the microversion
|
||||
package:
|
||||
|
||||
* The ``raise_http_status_code_if_not_version`` utility will raise a
|
||||
http status code if the requested microversion is not within a
|
||||
described version window.
|
||||
* The ``version_handler`` decorator makes it possible to have
|
||||
multiple different handler methods of the same (fully-qualified by
|
||||
package) name, each available for a different microversion window.
|
||||
If a request wants a microversion that's not available, a 404
|
||||
response is returned. There is a unit test in place which will
|
||||
fail if there are version intersections.
|
||||
|
||||
Adding a New Handler
|
||||
====================
|
||||
|
||||
Adding a new URL or a new method (e.g, ``PATCH``) to an existing URL
|
||||
requires adding a new handler function. In either case a new microversion and
|
||||
release note is required. When adding an entirely new route a request for a
|
||||
lower microversion should return a ``404``. When adding a new method to an
|
||||
existing URL a request for a lower microversion should return a ``405``.
|
||||
|
||||
In either case, the ``ROUTE_DECLARATIONS`` dictionary in the
|
||||
`nova.api.openstack.placement.handler` module should be updated to point to a
|
||||
function within a module that contains handlers for the type of entity
|
||||
identified by the URL. Collection and individual entity handlers of the same
|
||||
type should be in the same module.
|
||||
|
||||
As mentioned above, the handler function should be decorated with
|
||||
``@wsgi_wrapper.PlacementWsgify``, take a single argument ``req`` which is a
|
||||
WebOb `Request`_ object, and return a WebOb `Response`_.
|
||||
|
||||
For ``PUT`` and ``POST`` methods, request bodies are expected to be JSON
|
||||
based on a content-type of ``application/json``. This may be enforced by using
|
||||
a decorator: ``@util.require_content('application/json')``. If the body is not
|
||||
`JSON`, a ``415`` response status is returned.
|
||||
|
||||
Response bodies are usually `JSON`. A handler can check the `Accept` header
|
||||
provided in a request using another decorator:
|
||||
``@util.check_accept('application/json')``. If the header does not allow
|
||||
`JSON`, a ``406`` response status is returned.
|
||||
|
||||
`JSON` sent in a request should be validated against a JSON Schema. A
|
||||
``util.extract_json`` method is available. This takes a request body and a
|
||||
schema. If multiple schema are used for different microversions of the same
|
||||
request, the caller is responsible for selecting the right one before calling
|
||||
``extract_json``.
|
||||
|
||||
When a handler needs to read or write the data store it should use methods on
|
||||
the objects found in the `nova.objects.resource_provider` package. Doing so
|
||||
requires a context which is provided to the handler method via the WSGI
|
||||
environment. It can be retrieved as follows::
|
||||
|
||||
context = req.environ['placement.context']
|
||||
|
||||
.. note:: If your change requires new methods or new objects in the
|
||||
`resource_provider` package, after you've made sure that you really
|
||||
do need those new methods or objects (you may not!) make those
|
||||
changes in a patch that is separate from and prior to the HTTP API
|
||||
change.
|
||||
|
||||
Testing of handler code is described in the next section.
|
||||
|
||||
Testing
|
||||
=======
|
||||
|
||||
Most of the handler code in the placement API is tested using `gabbi`_. Some
|
||||
utility code is tested with unit tests found in
|
||||
`nova/tests/unit/api/openstack/placement/`. The back-end objects are tested
|
||||
with a combination of unit and functional tests found in
|
||||
`nova/tests/unit/objects/test_resource_provider.py` and
|
||||
`nova/tests/functional/db`. Adding unit and non-gabbi functional tests is done
|
||||
in the same way as other aspects of nova.
|
||||
|
||||
Using Gabbi
|
||||
-----------
|
||||
|
||||
Gabbi was developed in the `telemetry`_ project to provide a declarative way to
|
||||
test HTTP APIs that preserves visibility of both the request and response of
|
||||
the HTTP interaction. Tests are written in YAML files where each file is an
|
||||
ordered suite of tests. Fixtures (such as a database) are set up and torn down
|
||||
at the beginning and end of each file, not each test. JSON response bodies can
|
||||
be evaluated with `JSONPath`_. The placement WSGI
|
||||
application is run via `wsgi-intercept`_, meaning that real HTTP requests are
|
||||
being made over a file handle that appears to Python to be a socket.
|
||||
|
||||
In the placement API the YAML files (aka "gabbits") can be found in
|
||||
`nova/tests/functional/api/openstack/placement/gabbits`. Fixture definitions are
|
||||
in `fixtures.py` in the parent directory. Tests are currently grouped by handlers
|
||||
(e.g., `resource-provider.yaml` and `inventory.yaml`). This is not a
|
||||
requirement and as we increase the number of tests it makes sense to have more
|
||||
YAML files with fewer tests, divided up by the arc of API interaction that they
|
||||
test.
|
||||
|
||||
The gabbi tests are integrated into the functional tox target, loaded via
|
||||
`nova/tests/functional/api/openstack/placement/test_placement_api.py`. If you
|
||||
want to run just the gabbi tests one way to do so is::
|
||||
|
||||
tox -efunctional test_placement_api
|
||||
|
||||
If you want to run just one yaml file (in this example `inventory.yaml`)::
|
||||
|
||||
tox -efunctional placement_api.inventory
|
||||
|
||||
It is also possible to run just one test from within one file. When you do this
|
||||
every test prior to the one you asked for will also be run. This is because
|
||||
the YAML represents a sequence of dependent requests. Select the test by using
|
||||
the name in the yaml file, replacing space with ``_``::
|
||||
|
||||
tox -efunctional placement_api.inventory_post_new_ipv4_address_inventory
|
||||
|
||||
.. note:: `.testr.conf` in the nova repository is configured such that each
|
||||
gabbi YAML is considered a group. Thus, all tests in the file will
|
||||
be run in the same process when running testr concurrently (the
|
||||
default).
|
||||
|
||||
Writing More Gabbi Tests
|
||||
------------------------
|
||||
|
||||
The docs for `gabbi`_ try to be complete and explain the `syntax`_ in some
|
||||
depth. Where something is missing or confusing, please log a `bug`_.
|
||||
|
||||
While it is possible to test all aspects of a response (all the response
|
||||
headers, the status code, every attribute in a JSON structure) in one single
|
||||
test, doing so will likely make the test harder to read and will certainly make
|
||||
debugging more challenging. If there are multiple things that need to be
|
||||
asserted, making multiple requests is reasonable. Since database set up is only
|
||||
happening once per file (instead of once per test) and since there's no TCP
|
||||
overhead, the tests run quickly.
|
||||
|
||||
While `fixtures`_ can be used to establish entities that are required for
|
||||
tests, creating those entities via the HTTP API results in tests which are more
|
||||
descriptive. For example the `inventory.yaml` file creates the resource
|
||||
provider to which it will then add inventory. This makes it easy to explore a
|
||||
sequence of interactions and a variety of responses with the tests:
|
||||
|
||||
* create a resource provider
|
||||
* confirm it has empty inventory
|
||||
* add inventory to the resource provider (in a few different ways)
|
||||
* confirm the resource provider now has inventory
|
||||
* modify the inventory
|
||||
* delete the inventory
|
||||
* confirm the resource provider now has empty inventory
|
||||
|
||||
Nothing special is required to add a new set of tests: create a YAML file with
|
||||
a unique name in the same directory as the others. The other files can provide
|
||||
examples. Gabbi can provide a useful way of doing test driven development of a
|
||||
new handler: create a YAML file that describes the desired URLs and behavior
|
||||
and write the code to make it pass.
|
||||
|
||||
It's also possible to use gabbi against a running placement service, for
|
||||
example in devstack. See `gabbi-run`_ to get started.
|
||||
|
||||
Futures
|
||||
=======
|
||||
|
||||
Since before it was created there has been a long term goal for the placement
|
||||
service to be extracted to its own repository and operate as its own
|
||||
independent service. There are many reasons for this, but two main ones are:
|
||||
|
||||
* Multiple projects, not just nova, will eventually need to manage resource
|
||||
providers using the placement API.
|
||||
* A separate service helps to maintain and preserve a strong contract between
|
||||
the placement service and the consumers of the service.
|
||||
|
||||
To lessen the pain of the eventual extraction of placement the service has been
|
||||
developed in a way to limit dependency on the rest of the nova codebase and be
|
||||
self-contained:
|
||||
|
||||
* Most code is in `nova/api/openstack/placement` except for oslo versioned
|
||||
object code in `nova/objects/resource_provider.py`.
|
||||
* Database query code is kept within the objects.
|
||||
* The methods on the objects are not remotable, as the only intended caller is
|
||||
the placement API code.
|
||||
|
||||
There are some exceptions to the self-contained rule (which will have to be
|
||||
addressed if the extraction ever happens):
|
||||
|
||||
* Exceptions unique to the placement API are still within the `nova.exceptions`
|
||||
package.
|
||||
* Code related to a resource class cache is within the `nova.db` package.
|
||||
* Database models, migrations and tables use the nova api database.
|
||||
* The nova `FaultWrapper` middleware is being used.
|
||||
* `nova.i18n` package provides the ``_`` and related functions.
|
||||
* `nova.conf` is used for configuration.
|
||||
* Unit and functional tests depend on fixtures and other functionality in base
|
||||
classes provided by nova.
|
||||
|
||||
When creating new code for the placement service, please be aware of the plan
|
||||
for an eventual extraction and avoid creating unnecessary interdependencies.
|
||||
|
||||
.. _WSGI: https://www.python.org/dev/peps/pep-3333/
|
||||
.. _versioned objects: http://docs.openstack.org/developer/oslo.versionedobjects/
|
||||
.. _wsgify: http://docs.webob.org/en/latest/api/dec.html
|
||||
.. _WebOb: http://docs.webob.org/en/latest/
|
||||
.. _Request: http://docs.webob.org/en/latest/reference.html#request
|
||||
.. _Response: http://docs.webob.org/en/latest/#response
|
||||
.. _microversions: http://specs.openstack.org/openstack/api-wg/guidelines/microversion_specification.html
|
||||
.. _release note: http://docs.openstack.org/developer/reno/usage.html
|
||||
.. _gabbi: https://gabbi.readthedocs.io/
|
||||
.. _telemetry: http://specs.openstack.org/openstack/telemetry-specs/specs/kilo/declarative-http-tests.html
|
||||
.. _wsgi-intercept: http://wsgi-intercept.readthedocs.io/
|
||||
.. _syntax: https://gabbi.readthedocs.io/en/latest/format.html
|
||||
.. _bug: https://github.com/cdent/gabbi/issues
|
||||
.. _fixtures: http://gabbi.readthedocs.io/en/latest/fixtures.html
|
||||
.. _JSONPath: http://goessner.net/articles/JsonPath/
|
||||
.. _gabbi-run: http://gabbi.readthedocs.io/en/latest/runner.html
|
||||
.. _errors: http://specs.openstack.org/openstack/api-wg/guidelines/errors.html
|
@ -85,7 +85,7 @@ integration testing efforts.
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
test_strategy
|
||||
contributor/testing
|
||||
feature_classification
|
||||
support-matrix
|
||||
|
||||
@ -98,11 +98,11 @@ actually does, and why.
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
how_to_get_involved
|
||||
process
|
||||
contributor/how-to-get-involved
|
||||
contributor/process
|
||||
architecture
|
||||
project_scope
|
||||
development.environment
|
||||
contributor/project-scope
|
||||
contributor/development-environment
|
||||
|
||||
Development Policies
|
||||
--------------------
|
||||
@ -124,11 +124,11 @@ community, while keeping users happy and keeping developers productive.
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
process
|
||||
blueprints
|
||||
policies
|
||||
code-review
|
||||
releasenotes
|
||||
contributor/process
|
||||
contributor/blueprints
|
||||
contributor/policies
|
||||
contributor/code-review
|
||||
contributor/releasenotes
|
||||
|
||||
Architecture Concepts
|
||||
----------------------
|
||||
@ -146,7 +146,7 @@ Open Development.
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
addmethod.openstackapi
|
||||
contributor/api-2
|
||||
rpc
|
||||
block_device_mapping
|
||||
conductor
|
||||
@ -155,7 +155,7 @@ Open Development.
|
||||
i18n
|
||||
notifications
|
||||
placement
|
||||
placement_dev
|
||||
contributor/placement
|
||||
quotas
|
||||
threading
|
||||
vmstates
|
||||
@ -176,8 +176,8 @@ these are a great place to start reading up on the current plans.
|
||||
|
||||
cells
|
||||
upgrade
|
||||
api_plugins
|
||||
api_microversion_dev
|
||||
contributor/api
|
||||
contributor/microversions
|
||||
policy_enforcement
|
||||
stable_api
|
||||
scheduler_evolution
|
||||
@ -189,9 +189,9 @@ Advanced testing and guides
|
||||
:maxdepth: 1
|
||||
|
||||
gmr
|
||||
testing/libvirt-numa
|
||||
testing/serial-console
|
||||
testing/zero-downtime-upgrade
|
||||
contributor/testing/libvirt-numa
|
||||
contributor/testing/serial-console
|
||||
contributor/testing/zero-downtime-upgrade
|
||||
|
||||
Sample Configuration File
|
||||
-------------------------
|
||||
|
Loading…
x
Reference in New Issue
Block a user