This is generated using: $ tox -e move-implemented-specs -- pike -v Change-Id: I4f3e159430dbe7146e98f93645d8f6be06acb097
491 lines
15 KiB
ReStructuredText
491 lines
15 KiB
ReStructuredText
..
|
|
This work is licensed under a Creative Commons Attribution 3.0 Unported
|
|
License.
|
|
|
|
http://creativecommons.org/licenses/by/3.0/legalcode
|
|
|
|
=============================================
|
|
Use uuids in services and os-hypervisors APIs
|
|
=============================================
|
|
|
|
`<https://blueprints.launchpad.net/nova/+spec/service-hyper-uuid-in-api>`_
|
|
|
|
To work with services and hypervisors (compute nodes) in the compute REST API
|
|
we currently expose and take primary key IDs. In a multi-cell
|
|
deployment, these IDs are not unique. This spec proposes exposing a uuid for
|
|
services and hypervisors in the REST API to uniquely identify a resource
|
|
regardless of which cell it is in.
|
|
|
|
|
|
Problem description
|
|
===================
|
|
|
|
We currently leak database id fields (primary keys) out of the compute REST
|
|
API for services and compute_nodes which are all in a cell database (the
|
|
'nova' database in a cells v2 deployment). These are in the `os-services` and
|
|
`os-hypervisors` APIs, respectively.
|
|
|
|
For example, to delete a service record, you must issue a DELETE request to
|
|
``/os-services/{service_id}`` to delete the service record with that id.
|
|
|
|
The `os-hypervisors` API exposes the id in GET (index) requests and uses it in
|
|
the "show" and "uptime" methods to look up the ComputeNode object by that id.
|
|
|
|
This is ugly but functional in a single-cell deployment. However, in a
|
|
multi-cell deployment, we have no context on which cell we should query to get
|
|
service/node details from, since you could have multiple cells each with a
|
|
nova-compute service and compute node with id 1, so which cell do you pick to
|
|
delete the service or show details about the hypervisor?
|
|
|
|
Use Cases
|
|
---------
|
|
|
|
As a cloud administrator, I want to uniquely identify the resources in my
|
|
cloud regardless of which cell they are in and be able to get details about
|
|
and delete them.
|
|
|
|
Proposed change
|
|
===============
|
|
|
|
This blueprint proposes to add a microversion to the compute REST API which
|
|
replaces the usage of the id field with a uuid field. The uuid would be
|
|
returned instead of the id in GET responses and also taken as input for the id
|
|
in CRUD APIs.
|
|
|
|
Then when a request to delete a service is made, if the uuid is provided we
|
|
can simply iterate cells until we find the service, or error with a 404.
|
|
|
|
Before the microversion, if an id is passed and there is only one cell, or no
|
|
duplicates in multiple cells, we will continue to honor the request. But if an
|
|
id is passed on the request (before the microversion) and we cannot uniquely
|
|
identify the record out of multiple cells, we error with a 400. This is
|
|
similar behavior to how creating a server works when a network or port is not
|
|
provided and there are multiple networks available to the project, we fail
|
|
with a 400 "NetworkAmbiguous" error.
|
|
|
|
The compute_nodes table already has a uuid field. The services table, however,
|
|
does not, so as part of this blueprint we will need to add a
|
|
uuid column to that table and corresponding versioned object.
|
|
|
|
Alternatives
|
|
------------
|
|
|
|
Alternatives to exposing just the basic uuid and using it to iterate over
|
|
potentially multiple cells until we find a match, is to encode the cell uuid
|
|
in the resource uuid. For example, if we could simply return
|
|
``{cell_uuid}-{resource_uuid}``.
|
|
|
|
Then rather than iterating all cells to find the resource, we could decode the
|
|
input uuid to get the cell we need.
|
|
|
|
This is not a recommended alternative because it encodes the cell in the REST
|
|
API which is something we have said in the past we did not want to do, and is
|
|
similar to how cells v1 does namespacing on cells. It would also mean that
|
|
parts of the compute API are encoding a cell uuid and others, like the
|
|
`servers` API, are not. This could lead to maintenance issues in the actual
|
|
code since we would have different lookup operations for different resources.
|
|
|
|
Another alternative is creating mapping tables in the Nova API database, like
|
|
the ``host_mappings`` and ``instance_mappings`` tables. This alternative is
|
|
not recommended, at least not at this time, because the need for working with
|
|
service records should be relatively small.
|
|
|
|
Data model impact
|
|
-----------------
|
|
|
|
The `services` table in the cell (nova) database will have a nullable uuid
|
|
column added. The column will be nullable due to existing records which do
|
|
not have the uuid field.
|
|
|
|
We can migrate the data on access through the versioned object, and/or
|
|
provide online data migrations to add uuids to existing records during an
|
|
upgrade.
|
|
|
|
REST API impact
|
|
---------------
|
|
|
|
os-hypervisors
|
|
~~~~~~~~~~~~~~
|
|
|
|
There are only ``GET`` methods in this API. They will all be changed
|
|
to return the uuid value for the `id` field and take as input a uuid
|
|
value for the ``{hypervisor_id}``. We cannot use the `query parameter
|
|
validation`_ added in Ocata to validate that the ID passed in is a uuid since
|
|
it is not be a query parameter. Therefore, we will need to validate the
|
|
input `id` value is a uuid in code.
|
|
|
|
The following APIs will also be changed::
|
|
|
|
* GET /os-hypervisors/{hypervisor_hostname_pattern}/search
|
|
* GET /os-hypervisors/{hypervisor_hostname_pattern}/servers
|
|
|
|
Both of those APIs return a list of matches given the hostname
|
|
search pattern. While not directly needed to the problem stated
|
|
in this spec, we will take the opportunity of the microversion change
|
|
in this API to make these better. The `hypervisor_hostname_pattern` will
|
|
change to a query parameter.
|
|
|
|
* Old: GET /os-hypervisors/{hypervisor_hostname_pattern}/search
|
|
|
|
* New: GET /os-hypervisors?hypervisor_hostname=xxx
|
|
|
|
Example request::
|
|
|
|
GET /os-hypervisors?hypervisor_hostname=london1.compute
|
|
|
|
Example response::
|
|
|
|
{
|
|
"hypervisors": [
|
|
{
|
|
"hypervisor_hostname": "london1.compute.1",
|
|
"id": "37c62dfd-105f-40c2-a749-0bd1c756e8ff",
|
|
"state": "up",
|
|
"status": "enabled"
|
|
}
|
|
]
|
|
}
|
|
|
|
|
|
* Old: GET /os-hypervisors/{hypervisor_hostname_pattern}/servers
|
|
|
|
* New: GET /os-hypervisors?hypervisor_hostname=xxx&with_servers=true
|
|
|
|
Example request::
|
|
|
|
GET /os-hypervisors?hypervisor_hostname=london1.compute&with_servers=true
|
|
|
|
Example response::
|
|
|
|
{
|
|
"hypervisors": [
|
|
{
|
|
"hypervisor_hostname": "london1.compute.1",
|
|
"id": "37c62dfd-105f-40c2-a749-0bd1c756e8ff",
|
|
"state": "up",
|
|
"status": "enabled",
|
|
"servers": [
|
|
{
|
|
"name": "test_server1",
|
|
"uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
|
|
},
|
|
{
|
|
"name": "test_server2",
|
|
"uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
|
|
.. _query parameter validation: https://specs.openstack.org/openstack/nova-specs/specs/ocata/implemented/consistent-query-parameters-validation.html
|
|
|
|
os-services
|
|
~~~~~~~~~~~
|
|
|
|
The following API methods which take as input and/or return the integer
|
|
primary key id in the response will be updated to take/return a uuid::
|
|
|
|
* GET /os-services
|
|
* DELETE /os-services/{service_id}
|
|
|
|
For example:
|
|
|
|
**GET /os-services**
|
|
|
|
Response::
|
|
|
|
{
|
|
"services": [
|
|
{
|
|
"id": "8e6e4ab6-0662-4ff5-8994-dde92bedada1",
|
|
"binary": "nova-scheduler",
|
|
"disabled_reason": "test1",
|
|
"host": "host1",
|
|
"state": "up",
|
|
"status": "disabled",
|
|
"updated_at": "2012-10-29T13:42:02.000000",
|
|
"forced_down": false,
|
|
"zone": "internal"
|
|
},
|
|
{
|
|
"id": "3fe90b52-1d67-4f03-9ed3-5fbf1a6fa1e1",
|
|
"binary": "nova-compute",
|
|
"disabled_reason": "test2",
|
|
"host": "host1",
|
|
"state": "up",
|
|
"status": "disabled",
|
|
"updated_at": "2012-10-29T13:42:05.000000",
|
|
"forced_down": false,
|
|
"zone": "nova"
|
|
},
|
|
]
|
|
}
|
|
|
|
**DELETE /os-services/3fe90b52-1d67-4f03-9ed3-5fbf1a6fa1e1**
|
|
|
|
There is no response for a successful delete operation.
|
|
|
|
|
|
The **action** APIs do not take an id to identify the service on which to
|
|
perform an action. These include::
|
|
|
|
* PUT /os-services/disable
|
|
* PUT /os-services/disable-log-reason
|
|
* PUT /os-services/enable
|
|
* PUT /os-services/force-down
|
|
|
|
Unlike the ``/servers/{server_id}/action`` APIs which take the action in
|
|
the request body, these APIs do not take a specific service id. The request
|
|
body contains a ``host`` and ``binary`` field to identify the service.
|
|
|
|
As part of this microversion, we will collapse those action APIs into a single
|
|
PUT method which supports all of the actions and takes a ``service_id`` as
|
|
input to uniquely identify the service rather than a body with the ``host``
|
|
and ``binary`` fields.
|
|
|
|
What follows are examples of the old and new formats for each action API.
|
|
|
|
* PUT /os-services/disable
|
|
|
|
Old request::
|
|
|
|
PUT /os-services/disable
|
|
{
|
|
"host": "host1",
|
|
"binary": "nova-compute"
|
|
}
|
|
|
|
New request::
|
|
|
|
PUT /os-services/{service_id}
|
|
{
|
|
"status": "disabled"
|
|
}
|
|
|
|
* PUT /os-services/disable-log-reason
|
|
|
|
Old request::
|
|
|
|
PUT /os-services/disable-log-reason
|
|
{
|
|
"host": "host1",
|
|
"binary": "nova-compute",
|
|
"disabled_reason": "test2"
|
|
}
|
|
|
|
New request::
|
|
|
|
PUT /os-services/{service_id}
|
|
{
|
|
"status": "disabled",
|
|
"disabled_reason": "test2"
|
|
}
|
|
|
|
* PUT /os-services/enable*
|
|
|
|
Old request::
|
|
|
|
PUT /os-services/enable
|
|
{
|
|
"host": "host1",
|
|
"binary": "nova-compute"
|
|
}
|
|
|
|
New request::
|
|
|
|
PUT /os-services/{service_id}
|
|
{
|
|
"status": "enabled"
|
|
}
|
|
|
|
* PUT /os-services/force-down
|
|
|
|
Old request::
|
|
|
|
PUT /os-services/force-down
|
|
{
|
|
"host": "host1",
|
|
"binary": "nova-compute",
|
|
"forced_down": true
|
|
}
|
|
|
|
New request::
|
|
|
|
PUT /os-services/{service_id}
|
|
{
|
|
"forced_down": true
|
|
}
|
|
|
|
We will also provide a full response for the PUT method now. For example:
|
|
|
|
* PUT /os-services/disable-log-reason
|
|
|
|
Old response::
|
|
|
|
{
|
|
"service": {
|
|
"binary": "nova-compute",
|
|
"disabled_reason": "test2",
|
|
"host": "host1",
|
|
"status": "disabled"
|
|
}
|
|
}
|
|
|
|
New response::
|
|
|
|
{
|
|
"service": {
|
|
"id": "ade63841-f3e4-47de-840f-815322afa569",
|
|
"binary": "nova-compute",
|
|
"disabled_reason": "test2",
|
|
"host": "host1",
|
|
"state": "up",
|
|
"status": "disabled",
|
|
"updated_at": "2012-10-29T13:42:05.000000",
|
|
"forced_down": false,
|
|
"zone": "nova"
|
|
}
|
|
}
|
|
|
|
|
|
Security impact
|
|
---------------
|
|
|
|
None
|
|
|
|
Notifications impact
|
|
--------------------
|
|
|
|
Services
|
|
~~~~~~~~
|
|
|
|
The ``service.update`` versioned notification payload will be updated to
|
|
include the new uuid field.
|
|
|
|
Hosts
|
|
~~~~~
|
|
|
|
There are legacy unversioned notifications for actions on a compute node,
|
|
such as ``HostAPI.set_enabled.start``. These are not converted to using
|
|
versioned notifications yet, so until they are, there are no changes needed.
|
|
|
|
Other end user impact
|
|
---------------------
|
|
|
|
Since the REST API changes do not change the 'id' key in the response, only
|
|
the value, there should not need to be any changes in python-novaclient.
|
|
|
|
Performance Impact
|
|
------------------
|
|
|
|
None. Since we do not have a mapping table for services in the nova_api
|
|
database, we already have to iterate cells looking for a match, as seen
|
|
in this change: https://review.openstack.org/#/c/442162/
|
|
|
|
Other deployer impact
|
|
---------------------
|
|
|
|
Once deployers have multiple cells, they may have to update tooling to
|
|
specify the microversion to uniquely identify hypervisors or services,
|
|
for example, to delete a service.
|
|
|
|
Developer impact
|
|
----------------
|
|
|
|
None
|
|
|
|
|
|
Implementation
|
|
==============
|
|
|
|
Assignee(s)
|
|
-----------
|
|
|
|
Primary assignee:
|
|
Matt Riedemann (mriedem)
|
|
|
|
Other contributors:
|
|
Dan Peschman (dpeschman)
|
|
|
|
Work Items
|
|
----------
|
|
|
|
* Write a database schema migration to add the services.uuid column.
|
|
* Add the uuid field to the Service object.
|
|
* Generate a uuid for new services if not specified during create().
|
|
* Generate and save a uuid for old services upon retrieval from the
|
|
database, like when compute nodes got a uuid [1]_.
|
|
* Add `get_by_uuid` methods to the ComputeNode and Service objects.
|
|
* Add an online data migration for service uuids like what we had for compute
|
|
nodes [2]_.
|
|
* Update the ``nova.compute.api.HostAPI`` methods which take an ID and check
|
|
if the ID is a uuid and if so, query for the resource using the
|
|
`get_by_uuid` method on the object, otherwise use `get_by_id` as today.
|
|
* Add the microversion to the `os-hypervisors` and `os-services` APIs
|
|
including validation to ensure the incoming id is a uuid. This also includes
|
|
changing the request format of the `os-services` PUT method. This is likely
|
|
going to be a large and relatively complicated change to review, but given
|
|
all of these changes are going to be in the same microversion we cannot
|
|
realistically break these changes up.
|
|
* Update the compute API response schema validation for hypervisors [3]_ and
|
|
services [4]_. Note that the Tempest response schema already allows for
|
|
integers or strings. As part of this change, we should update the response
|
|
schema validation in Tempest to be strict that the hypervisor and service id
|
|
should be a uuid after this new microversion.
|
|
|
|
|
|
Dependencies
|
|
============
|
|
|
|
None
|
|
|
|
|
|
Testing
|
|
=======
|
|
|
|
* Unit tests for negative scenarios, like not being able to find a service by
|
|
uuid in multiple cells. We should also test passing a non-uuid integer value
|
|
to the changed APIs with the new microversion to ensure the query parameter
|
|
validation makes that request fail with a 400 error.
|
|
* Functional testing for API samples to ensure the 'id' value in a response
|
|
after the microversion is a uuid and not an integer.
|
|
* Tempest API tests *may* be added, although we can probably handle that same
|
|
test coverage with in-tree functional tests.
|
|
* We will have to test all of the `os-services` PUT method changes with
|
|
in-tree functional tests because Tempest does not test disabling or forcing
|
|
down a compute service since that would break a concurrent multi-tenant
|
|
Tempest run.
|
|
|
|
|
|
Documentation Impact
|
|
====================
|
|
|
|
The `os-services`_ and `os-hypervisors`_ API reference docs will need to be
|
|
updated to note the new microversion takes as input and returns in the
|
|
response a uuid value for the 'id' key.
|
|
|
|
.. _os-services: https://developer.openstack.org/api-ref/compute/#compute-services-os-services
|
|
.. _os-hypervisors: https://developer.openstack.org/api-ref/compute/#hypervisors-os-hypervisors
|
|
|
|
|
|
References
|
|
==========
|
|
|
|
.. [1] https://github.com/openstack/nova/blob/13.0.0/nova/objects/compute_node.py#L243
|
|
.. [2] https://github.com/openstack/nova/blob/13.0.0/nova/db/sqlalchemy/api.py#L6436
|
|
.. [3] https://github.com/openstack/tempest/blob/15.0.0/tempest/lib/api_schema/response/compute/v2_1/hypervisors.py#L68
|
|
.. [4] https://github.com/openstack/tempest/blob/15.0.0/tempest/lib/api_schema/response/compute/v2_1/services.py#L27
|
|
|
|
|
|
History
|
|
=======
|
|
|
|
.. list-table:: Revisions
|
|
:header-rows: 1
|
|
|
|
* - Release Name
|
|
- Description
|
|
* - Pike
|
|
- Introduced
|