Files
nova/doc/source/placement_dev.rst
T
Chris Dent 2d0dfc632f [placement] placement_dev info for microversion handling
A section for placement_dev.rst describing how to manage
microversions, including available utility methods.

Change-Id: I8ace96ed7fd721c547cedf5285833a8baa52a70a
2017-01-14 10:55:15 -05:00

203 lines
9.2 KiB
ReStructuredText

..
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:`placment 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 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`.
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 `wsgify`_ 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
compute API. 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_404_if_not_version`` utility will cause a 404 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
====================
.. TODO(cdent) short step by step summary of adding a new endpoint
Testing
=======
.. TODO(cdent) a bit about gabbi tests and unit tests: how to use
and when to use what
Futures
=======
.. TODO(cdent) extraction to own thing plans
.. _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
.. _when a microversion is needed: http://docs.openstack.org/developer/nova/api_microversion_dev.html#when-do-i-need-a-new-microversion
.. _release note: http://docs.openstack.org/developer/reno/usage.html