Default volume type overrides¶
https://blueprints.launchpad.net/cinder/+spec/multiple-default-volume-types
Cinder’s current default volume type options are limited and are insufficient for big and/or complex deployments.
This spec proposes adding per project volume types.
Problem description¶
On complex and/or big OpenStack deployments we may have multiple availability zones (AZs) with many projects, increasing the complexity of properly structuring and managing resources.
One of the problems we encounter when having multiple AZs is that we usually want our projects to have volumes and instances collocated within the same AZ, but we can only define one default volume type, forcing our users to always remember to create volumes with a volume type (if it has the AZ defined in it) or to explicitly pass the AZ.
This is error prone, and users forgetting it will lead to unwanted cross AZ attachments, with the consequent performance issues and network throughput costs.
Right now there are 2 workarounds that users are adopting to mitigate this situation:
Disabling cross AZ attachments in Nova: For deployments that don’t want to do cross AZ instance live migrations, they can disable cross AZ attachments in Nova so the attachment to the instance fails, thus preventing performance issues on running VMs. This solution usually forces users to recreate the volume in the right AZ.
Unusable default volume type: Setting a default volume type with a self explanatory name, such as Need_To_Select_Volume_Type and setting it in a way that will fail the scheduling and leave the volume in an error state. This is less wasteful, but still not a nice user experience.
This spec addresses this specific problem.
Use Cases¶
On a country wide OpenStack deployment that spreads across multiple data centers where each one has its own storage array and OpenStack project, an administrator wants to make sure that volumes are created in their own data center by default. For this purpose the admin defines a per project default volume type where it has the appropriate extra specs.
On an OpenStack deployment with 3 different volume types (high performance, replicated, compressed) and different department (each with a different project) have different requirements, so the system admin wants to assign a different OpenStack volume type to each one of them.
Proposed change¶
The proposal is to add the possibility to override the existing Cinder default Volume Type on a per project basis to make it easier to manage complex deployments.
With the introduction of this new default volume type configuration, we’ll now have 2 different default volume types. From more specific to more generic these are:
Per project
Defined in cinder.conf (defaults to __DEFAULT__ type)
So when a user creates a new volume that has no defined volume type (explicit
or in the source), Cinder will look for the appropriate default first by
checking if there’s one defined in the DB for the specific project and use it,
if there isn’t one, it will continue like it does today, using the default type
from cinder.conf
.
Administrators and users must still be careful with the normal Cinder behavior when creating volumes, as Cinder will still only resort to using the default volume type if the user doesn’t select one on the request or if there’s no volume type in the source, which means that Cinder will not use any of those defaults if we:
Create a volume providing a volume type
Create a volume from a snapshot
Clone a volume
Create a volume from an image that has
cinder_img_volume_type
defined in its metadata.
By default the policy restricting access to set, unset, get or list all project default volume type will be set to system admins only.
Alternatives¶
An alternative would be allow setting default volume types overrides on a per user and per project basis, as well as globally.
Such alternative was discarded, given the added complexity and effort needed to implement to provide very little benefits, as configurable per user and global default volume types don’t seem to be a user necessity.
Data model impact¶
We will add a new table called default_volume_type
that will have 3 fields:
id
primary integer key.volume_type_id
foreign key string of length 36.project_id
string.
We won’t reuse existing volume_type_projects
table and just add an
is_default
field because then the combinations of private/public volume
types and defaults get complex as we update or remove them.
In terms of database migrations we’ll just have to create the table.
REST API impact¶
We’ll need a new set of REST API calls to provide the CRUD operations:
Set the default volume type (create or update) for a specific project.
Besides ensuring that the volume type exists and the project has access to it, this method will also call keystone to validate that the project exists.
PUT /v3/default-types/{project_id}
JSON body to set default type (by name or id) for a project:
{ "volume_type": "lvm" }
- Response Codes:
Success - 200 (with body)
{ "project_id": "248592b4-a6da-4c4c-abe0-9d8dbe0b74b4", "type_id": "f8a82360-0b1a-4649-8615-114341dd06e0" }
Error - 400 (volume type not found), 404 (project id not found)
Unset a default volume type. It will fail if there is not a default volume type for the given project or if the project no longer exists in keystone.
DELETE /v3/default-types/{project_id}
Response Codes: - Success - 204 - Error - 404 (project id not found)
Get volume types
Show default type for a project GET /v3/default-types/{project_id}
A request with project_id will return a single JSON object containing the project_id and type_id of the specified project. Returns 404 error code if the entry is not in the DB, independent of the existence of the project in keystone.
{ "project_id": "248592b4-a6da-4c4c-abe0-9d8dbe0b74b4", "type_id": "f8a82360-0b1a-4649-8615-114341dd06e0" }
Response Codes: - Success - 200 (with body) - Error - 404 (project id not found or no default type)
List all default types GET /v3/default-types
A request without a project_id will return all the volume type defaults defined. A sample response would be:
[ { "project_id": "248592b4-a6da-4c4c-abe0-9d8dbe0b74b4", "type_id": "f8a82360-0b1a-4649-8615-114341dd06e0" }, { "project_id": "1234567-4c4c-abcd-abe0-1a2b3c4d5e6ff", "type_id": "5c4df055-571e-4430-9823-416b82f337b2" } ]
Response Codes: - Success - 200 (with body)
Notice that we only list overrides, we won’t return the value of
default_volume_type
.
A user can get its effective default type using existing cinder
type-default
command: GET /v3/{project_id}/types/default
.
Security impact¶
None
Active/Active HA impact¶
None
Notifications impact¶
None
Other end user impact¶
There will be a new set of commands in the python-cinderclient to match the new REST API endpoints:
Set default:
cinder default-type-set <type-name> <project-id>
Unset default:
cinder default-type-unset <project-id>
List defaults:
cinder default-type-list [--project <project-id>]
Performance Impact¶
Create volume operations that don’t define a default volume type (explicitly or via a source) will have a tiny performance impact since we’ll add a DB query to get the defaults.
Other deployer impact¶
None
Developer impact¶
We should no longer refer directly to the default_volume_type
configuration
option throughout the code and instead use the get_default_volume_type
method from cinder.volume.volume_types
.
Implementation¶
Assignee(s)¶
- Primary assignee:
<whoami-rajat>
Work Items¶
Cinder service
Check if caller is authorized to do the operation: First we’ll check the normal policy to see if it’s a system admin, etc, but then we’ll have to check the project, and we’ll only authorize the operation if caller’s context has system scope.
For this we have introduced a new policy to check if the caller is a system admin and then leverage the get_project_hierarchy method in cinder.quota_utils to validate that the project actually exists (since the method does a get of the project).
Add the DB field and the DB migration.
Add 3 DB layer methods:
Set default volume type: Given a volume type id and a project id this method sets its default volume type.
It will try to update the
volume_type_id
for theproject_id
and if it fails because the row doesn’t exist, then it will create the DB row.Unset default volume type: This will set
deleted
anddeleted_at
fields for theproject_id
. If it fails because it doesn’t exist, the failure will not be propagated and it will be considered a success.Get projects default volume type: Returns a list of project and volume type ids limited by the
project_id
if it is provided.
Update
get_default_volume_type
to return the effective volume type for the current project. Basically calling the get project default type DB method, and if it returns None, then we’ll continue with the current code we have to use the one from the config.Updating the volume type methods to ensure we don’t try to delete a volume type that is used as a default, and making sure we don’t set as private a volume type that a project is using as a default, and such operations.
Ensure that
purge_deleted_rows
fromcinder.db.sqlalchemy.api
works as expected.Add a new API microversion and implement the 4 REST API methods.
Write appropriate unit-tests for the DB methods, REST API methods, and update existing tests for the changes we introduced.
Cinder client: Add the 3 commands mentioned earlier in the Other end user impact section.
Tempest tests: Add tempest tests as describe in the Testing section.
Documentation as describe in the Documentation impact section.
Dependencies¶
None
Testing¶
Besides writing the appropriate unit-tests for the DB methods, REST API methods, and update existing tests for the changes we introduced, we also need a series of Tempest tests to test existing functionality.
Confirm that the priority of the default types are observed:
Admin creates a custom volume type and sets it as the project’s default
Create an empty volume with a normal user and check that the volume type is the one we created, then delete it.
Create another volume with the admin user and see that it works the same.
Create an empty volume with the alternative project admin user and confirm it doesn’t use our custom volume type.
Unset the custom volume type we set in step 1.
Create an empty volume and check that the volume type is not the custom one.
Confirm that
cinder type-default
works as expected:Admin confirms that there are no default overrides for the project and alternative project.
Get current default using
type-default
.Create 2 custom volume types: #1 and #2
Set default volume type #1 for project and #2 for alternative project.
Request the type default with both projects and confirm we get the ones we have just set.
Unset the custom volume types.
Get current default using
type-default
and confirm is the same as in step 2.
Confirm that listing/showing default type overrides works as expected:
Admin confirms that there are no default overrides for the project by first listing the default-types, and then by showing the default type for a project (we’ll get 404), for both the normal project and the alternative.
Create 2 custom volume types: #1 and #2
Set default volume type #1 for project and #2 for alternative project.
Admin lists all default volume type and validates them.
Admin gets default volume type for project and confirms that it only gets that one.
Repeat previous 2 steps for the alternative project.
Unset the default types.
Confirm that default type list returns empty list.
Confirm that showing default for a project id returns 404.
Show default for a fake project id and confirm we get 404 error code.
Documentation Impact¶
A description of the default volume types overrides behavior will be added to
the admin section, for example on path
doc/source/admin/default-volume-types.rst
.
This file will be linked from doc/source/admin/index.rst
and
doc/source/configuration/index.rst
since it is part of the configuration
but it’s also an administrative task.
The new CLI commands will be listed and explained in
doc/source/cli/cli-manage-volumes.rst
with examples of the new CLI
commands.
Additionally the new REST API calls will need to be documented in
api-ref/source/v3/default-volume-types.inc
and samples added to
api-ref/source/v3/samples
.
And the microversion history will need to be updated in
cinder/api/openstack/rest_api_version_history.rst
.