747 lines
19 KiB
ReStructuredText
747 lines
19 KiB
ReStructuredText
..
|
|
This work is licensed under a Creative Commons Attribution 3.0 Unported
|
|
License.
|
|
|
|
http://creativecommons.org/licenses/by/3.0/legalcode
|
|
|
|
==========================================
|
|
Add quota support for Barbican resources
|
|
==========================================
|
|
|
|
https://blueprints.launchpad.net/barbican/+spec/quota-support-on-barbican-resources
|
|
|
|
Barbican REST API doesn't impose any upper limit on the number of resources
|
|
allowed per project. This could result in resource explosion. This blueprint
|
|
proposes a way to specify and enforce quotas with projects. Quotas are
|
|
operational limits so that cloud resources are optimized.
|
|
|
|
|
|
Problem Description
|
|
===================
|
|
|
|
Here are few scenarios that could impact the normal functioning of the
|
|
Barbican server:
|
|
|
|
* A client could place requests for several thousands of orders to create
|
|
secrets for a single project. This might overwhelm the Barbican server
|
|
both in terms of processing time and disk space consumed
|
|
|
|
* If a buggy client script runs amok and attempts to create a generic
|
|
type container with no associated secret, it could quickly fill-up
|
|
the Barbican database!
|
|
|
|
* If a user creates a large number of projects and further creates a
|
|
large number of Barbican resources per these projects, that could
|
|
impact other genuine users
|
|
|
|
Note: The last point could be avoided if keystone could enforce a
|
|
upper limit on the number of projects that an user could create. Hence
|
|
this is treated as a lower priority for Barbican for now. This spec
|
|
aims to implement project level quotas first. A later spec is expected
|
|
to add support for user level quotas.
|
|
|
|
This is similar to the quota enforcement done by nova and cinder
|
|
services.
|
|
|
|
|
|
Proposed Change
|
|
===============
|
|
|
|
Introduce quotas for all Barbican resources. The quotas should have
|
|
reasonably high values as defaults. The following resources will
|
|
have quota support:
|
|
|
|
* secrets
|
|
* orders
|
|
* containers
|
|
* transport_keys
|
|
* consumers
|
|
|
|
*Note:*
|
|
|
|
This proposal is a simpler subset of the quota implementation done
|
|
by oslo.common.quota.py. Barbican does not have any reservable resources
|
|
and so the quotas are much simpler in that there is no usage tracking and
|
|
reservations. Also, project-user level quota enforcement is not covered
|
|
by this spec.
|
|
|
|
***Enforcing quotas:***
|
|
|
|
Barbican API controllers will be updated with quota logic for the
|
|
create resource methods. Once implemented, the quota check will work
|
|
as follows for resource creation requests:
|
|
|
|
1. Get the quotas for the project retrieved from the auth context.
|
|
If per-project quotas have not been setup, use default quotas
|
|
2. Get a count of the resources for the context project
|
|
Note: Currently secrets that expire or are removed are not hard removed
|
|
from the database, but rather are soft deleted, so they still are using
|
|
resources within Barbican and would be counted against the project's
|
|
quota. However, this is deployment dependent. A deployment could have a
|
|
process that hard removes such resources after a period of time
|
|
3. If the count equals or exceeds the quotas, reject the request with
|
|
the following error message:
|
|
HTTP 403 Forbidden
|
|
{"error": "Quota exceeded for <project-id>. Only <count> <resource>s
|
|
are allowed"
|
|
}
|
|
4. Continue with the resource creation
|
|
|
|
|
|
Update the Barbican config file to include the following section for
|
|
quota limits:
|
|
|
|
::
|
|
|
|
[quotas]
|
|
enabled = true
|
|
|
|
# number of secrets allowed per project
|
|
quota_secrets = 500
|
|
|
|
# number of orders allowed per project
|
|
quota_orders = 100
|
|
|
|
# number of containers allowed per project
|
|
quota_containers = -1
|
|
# Note, a negative value signifies unlimited
|
|
|
|
# number of transport_keys allowed per project
|
|
quota_transport_keys = 100
|
|
|
|
# number of consumers allowed per project
|
|
quota_consumers = 100
|
|
|
|
A number >=0 for the quota_<value> indicates the max
|
|
limit for that resource and a negative value means unlimited. A value
|
|
of zero indicates a maximum of zero instances of that resource, effectively
|
|
disabling creation of that entity.
|
|
|
|
While these generic quotas apply to all projects, there will
|
|
also be support to enforce quotas per project.
|
|
The priority in which the quotas are enforced is then:
|
|
[per project quotas] => [default quotas]
|
|
|
|
where,
|
|
"per project quotas" - these quotas are directly associated with a particular
|
|
project id. Any changes to these quotas will impact only that project.
|
|
|
|
"default quotas" - if no per project quotas are specified, the
|
|
default quotas are used. These would typically be the values supplied in the
|
|
config file.
|
|
|
|
The default quotas are stored in the config file (as shown above) but
|
|
per-project quotas are stored in db.
|
|
|
|
A REST API for Barbican administrators for the quota CRUD operations will be
|
|
implemented as well. Non-admin users will be provided with a REST API to get
|
|
their own effective quotas for various resources. Keystone RBAC checks will
|
|
be employed to decide if a caller has the required admin role to perform
|
|
these admin-only operations. The admin endpoint of Barbican will not be used.
|
|
The details of this is discussed in a later section below.
|
|
|
|
|
|
Alternatives
|
|
------------
|
|
|
|
An attempt was made to create an oslo.common library with quota support
|
|
for all OpenStack projects. The first attempt was committed to oslo, however
|
|
it has been deprecated and has not been adopted by any projects. A second
|
|
attempt was started, but has been put on hold with no current plans to restart.
|
|
There is no other common OpenStack library implementing quotas for Barbican to adopt.
|
|
|
|
The quota configuration and logic will be derived by looking at quota
|
|
implementations done by other OpenStack projects like nova, cinder
|
|
and neutron. A simplified implementation will developed for Barbican by
|
|
using APIs and logic similar to Nova's implementation, while removing unneeded
|
|
features, such as pluggable backend drivers and resource reservation.
|
|
|
|
Another alternative is an initiative by Kevin Mitchell from Rackspace
|
|
https://wiki.openstack.org/wiki/Boson. However, the Nova and Cinder design
|
|
is more usable for Barbican.
|
|
|
|
|
|
Data model impact
|
|
-----------------
|
|
|
|
The following new data models will be added:
|
|
|
|
* ProjectQuota
|
|
|
|
Represents a single quota override for a project.
|
|
|
|
If there is no row for a given project id and resource, then the
|
|
default for the deployment is used. If the row is present but the hard
|
|
limit is "-1" (no quotes), then the resource is unlimited.
|
|
|
|
Schema: (table name: **project_quota**)
|
|
|
|
* id: Integer, Primary Key
|
|
* project_id: String(255)
|
|
* resource: String(255), nullable=False, one of "secrets","orders",
|
|
"containers","transport_keys", "consumers"
|
|
* hard_limit: Integer
|
|
|
|
**Constraints**: project_id + resource should be unique
|
|
|
|
|
|
* Changes to existing models:
|
|
|
|
No existing models will be impacted by this addition. However, it needs
|
|
to be investigated if new indexes need to be built to speed up resource
|
|
consumption lookups.
|
|
|
|
|
|
REST API impact
|
|
---------------
|
|
|
|
The following new REST API will be implemented to manage quotas CRUD
|
|
operations. Please note that except for the first GET API, all the
|
|
other APIs require the caller to have admin role.
|
|
|
|
* Get effective quotas (any Barbican user)
|
|
|
|
* Returns effective resource quotas for the caller for the specified
|
|
project. If there are no project specific quotas returns the
|
|
deployment default resource limits.
|
|
|
|
* GET /v1/quotas
|
|
|
|
* Normal http response code(s)
|
|
200 OK
|
|
|
|
* Expected error http response code(s)
|
|
|
|
* 401 Unauthorized - If the auth token is not present or invalid
|
|
* 404 Not Found - If using unauthenticated context and X-Project-Id
|
|
header is not present in the request
|
|
|
|
* Required request headers
|
|
|
|
X-Auth-Token, if using keystone auth
|
|
|
|
X-Project-Id, if using unauthenticated context
|
|
|
|
* Parameters
|
|
|
|
None
|
|
|
|
* JSON schema definition for the body data if allowed
|
|
|
|
None
|
|
|
|
* JSON schema definition for the response data if any
|
|
|
|
EXAMPLE::
|
|
|
|
{
|
|
'type': 'object',
|
|
'properties': {
|
|
'quotas': {
|
|
'type': 'object',
|
|
'properties': {
|
|
'secrets': {'type':'integer'}
|
|
'orders': {'type':'integer'},
|
|
'containers': {'type':'integer'},
|
|
'transport_keys': {'type':'integer'}
|
|
'consumers': {'type':'integer'}
|
|
},
|
|
'additionalProperties': False
|
|
}
|
|
},
|
|
'additionalProperties': False
|
|
}
|
|
|
|
* Example 1::
|
|
|
|
A non-admin user checking the resource quotas using a token scoped to a
|
|
particular project
|
|
|
|
Request:
|
|
|
|
GET /v1/quotas
|
|
|
|
X-Auth-Token:<token>
|
|
|
|
Response:
|
|
|
|
200 OK
|
|
|
|
Content-Type: application/json
|
|
|
|
{
|
|
"quotas": {
|
|
"secrets": 10,
|
|
"orders": 20,
|
|
"containers": 10,
|
|
"transport_keys": 10,
|
|
"consumers": -1
|
|
}
|
|
}
|
|
|
|
* List all project quotas (admin only)
|
|
|
|
* Lists all project level resource quotas across all users for all
|
|
projects. If there are only project specific quotas for few resources
|
|
for a project, this call will return defaults for other resources in that
|
|
project.
|
|
|
|
* GET /v1/project-quotas?limit=x&offset=y (Admin only)
|
|
|
|
* Normal http response code(s)
|
|
200 OK
|
|
|
|
* Expected error http response code(s)
|
|
|
|
* 401 Unauthorized - If the auth token is not present or invalid
|
|
* 404 Not Found - If using unauthenticated context and X-Project-Id
|
|
header is not present in the request
|
|
|
|
* Required request headers
|
|
|
|
X-Auth-Token, if using keystone auth
|
|
|
|
|
|
* Parameters
|
|
|
|
limit(optional), integer, maximum number of records retrieved
|
|
offset(optional), integer, number of records to skip
|
|
|
|
* JSON schema definition for the body data if allowed
|
|
|
|
None
|
|
|
|
* JSON schema definition for the response data if any
|
|
|
|
|
|
EXAMPLE::
|
|
|
|
{
|
|
'type': 'object',
|
|
'properties': {
|
|
'project-quotas': {
|
|
'type': 'array'
|
|
'items': {
|
|
'type': 'object',
|
|
'properties': {
|
|
'project-id': {'type':'string'},
|
|
'project-quotas': {
|
|
'type':'object',
|
|
'properties': {
|
|
'secrets': {'type': 'integer'},
|
|
'orders': {'type': 'integer'},
|
|
'containers': {'type': 'integer'},
|
|
'transport_keys': {'type': 'integer'},
|
|
'consumers': {'type': 'integer'}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
'additionalProperties': False
|
|
}
|
|
|
|
* Example 1::
|
|
|
|
An admin user listing all the project quotas
|
|
|
|
Request:
|
|
|
|
GET /v1/project-quotas
|
|
|
|
X-Auth-Token:<token>
|
|
|
|
Response:
|
|
|
|
200 OK
|
|
|
|
Content-Type: application/json
|
|
|
|
{
|
|
"project-quotas": [
|
|
{
|
|
"project-id": "1234",
|
|
"project-quotas": {
|
|
"secrets": 2000,
|
|
"orders": 1000,
|
|
"containers": 500,
|
|
"transport_keys": 100,
|
|
"consumers": 10000
|
|
}
|
|
},
|
|
{
|
|
"project-id": "5678",
|
|
"project-quotas": {
|
|
"secrets": 200,
|
|
"orders": 100,
|
|
"containers": 100,
|
|
"transport_keys": 50,
|
|
"consumers": 500
|
|
}
|
|
},
|
|
]
|
|
}
|
|
|
|
|
|
* Get quotas for a specific project (admin only)
|
|
|
|
* Returns a list of all resource quotas for the specified project. If there
|
|
are only project specific quotas for few resources for a project, this call
|
|
will return defaults for other resources in that project.
|
|
|
|
* GET /v1/project-quotas/{project-id}
|
|
|
|
* Normal http response code(s)
|
|
200 OK
|
|
|
|
* Expected error http response code(s)
|
|
|
|
* 401 Unauthorized - If the auth token is not present or invalid
|
|
* 404 Not Found - If using unauthenticated context and X-Project-Id
|
|
header is not present in the request
|
|
|
|
* Required request headers
|
|
|
|
X-Auth-Token, if using keystone auth
|
|
|
|
X-Project-Id, if using unauthenticated context
|
|
|
|
* JSON schema definition for the body data if allowed
|
|
None
|
|
|
|
* JSON schema definition for the response data if any::
|
|
|
|
{
|
|
'type': 'object',
|
|
'properties': {
|
|
'project-quotas': {
|
|
'type':'object',
|
|
'properties': {
|
|
'secrets': {'type': 'integer'},
|
|
'orders': {'type': 'integer'},
|
|
'containers': {'type': 'integer'},
|
|
'transport_keys': {'type': 'integer'},
|
|
'consumers': {'type': 'integer'}
|
|
}
|
|
}
|
|
},
|
|
'additionalProperties': False
|
|
}
|
|
|
|
* Example::
|
|
|
|
Request:
|
|
|
|
GET /v1/project-quotas/1234
|
|
|
|
X-Auth-Token:<token>
|
|
|
|
Response:
|
|
|
|
200 OK
|
|
|
|
Content-Type: application/json
|
|
|
|
{
|
|
"project-quotas": {
|
|
"secrets": 10,
|
|
"orders": 20,
|
|
"containers": 10,
|
|
"transport_keys": 5,
|
|
"consumers": 10
|
|
}
|
|
}
|
|
|
|
|
|
* Update/Set quotas for a specific project (admin only)
|
|
|
|
* Updates and returns a list of resource quotas for the specified project.
|
|
It is not required to specify limits for all Barbican resources. If a
|
|
resource is not specified, the default limits are used for that
|
|
resource.
|
|
|
|
* PUT /v1/project-quotas/{project-id}
|
|
|
|
* Normal http response code(s)
|
|
204 No Content
|
|
|
|
* Expected error http response code(s)
|
|
|
|
* 401 Unauthorized - If the auth token is not present or invalid
|
|
* 404 Not Found - If using unauthenticated context and X-Project-Id
|
|
header is not present in the request
|
|
* 400 Bad Request - If the request payload doesn't confirm to schema
|
|
|
|
* Required request headers
|
|
|
|
X-Auth-Token, if using keystone auth
|
|
|
|
X-Project-Id, if using unauthenticated context
|
|
|
|
Content-Type, application/json
|
|
|
|
* JSON schema definition for the body data if allowed::
|
|
|
|
{
|
|
'type': 'object',
|
|
'properties': {
|
|
'project-quotas': {
|
|
'type':'object',
|
|
'properties': {
|
|
'secrets': {'type': 'integer'},
|
|
'orders': {'type': 'integer'},
|
|
'containers': {'type': 'integer'},
|
|
'transport_keys': {'type': 'integer'},
|
|
'consumers': {'type': 'integer'}
|
|
}
|
|
}
|
|
},
|
|
'additionalProperties': False
|
|
}
|
|
|
|
|
|
* JSON schema definition for the response data if any::
|
|
None
|
|
|
|
* Example::
|
|
|
|
Request:
|
|
|
|
PUT /v1/project-quotas/1234
|
|
|
|
X-Auth-Token:<token>
|
|
|
|
Body::
|
|
|
|
{
|
|
"project-quotas": {
|
|
"secrets": 50,
|
|
"orders": 10,
|
|
"containers": 20
|
|
}
|
|
}
|
|
|
|
|
|
Response:
|
|
|
|
200 OK
|
|
|
|
{
|
|
"project-quotas": {
|
|
"secrets": 10,
|
|
"orders": 20,
|
|
"containers": 10,
|
|
"transport_keys": 5,
|
|
"consumers": 10
|
|
}
|
|
}
|
|
|
|
|
|
* Delete quotas for a specific project (admin only)
|
|
|
|
* Deletes project specific resource quotas for the specified project.
|
|
After this call succeeds, the default resource quotas will be
|
|
returned for subsequent calls by the user to list effective quotas.
|
|
|
|
* DELETE v1/project-quotas/{project-id}
|
|
|
|
* Parameters
|
|
None
|
|
|
|
* Normal http response code(s)
|
|
204 No Content
|
|
|
|
* Expected error http response code(s)
|
|
|
|
* 401 Unauthorized - If the auth token is not present or invalid
|
|
* 404 Not Found - If using unauthenticated context and X-Project-Id
|
|
header is not present in the request
|
|
|
|
* Required request headers
|
|
|
|
X-Auth-Token, if using keystone auth
|
|
|
|
X-Project-Id, if using unauthenticated context
|
|
|
|
* Parameters
|
|
|
|
None
|
|
|
|
* JSON schema definition for the body data if allowed
|
|
|
|
None
|
|
|
|
* JSON schema definition for the response data if any
|
|
|
|
None
|
|
|
|
* Example 1::
|
|
|
|
Request:
|
|
|
|
DELETE v1/project-quotas/1234
|
|
|
|
X-Auth-Token:<token>
|
|
|
|
|
|
Response:
|
|
|
|
204 No Content
|
|
|
|
|
|
|
|
* Policy changes
|
|
|
|
For all admin-only APIs, the caller is expected to have a barbican admin
|
|
role. The check for this will be added to the Barbican policy.json
|
|
|
|
|
|
Once implemented and enforced, all Barbican resource creation API could return
|
|
a new error message back to the client if the request exceeded the allowed
|
|
quota limits.
|
|
|
|
Example::
|
|
|
|
Request::
|
|
|
|
POST /v1/secrets
|
|
|
|
X-Auth-Token: <token>
|
|
|
|
Content-Type: application/json
|
|
|
|
{
|
|
# payload to create secret
|
|
}
|
|
|
|
Response::
|
|
|
|
403 Forbidden
|
|
|
|
Retry-After: 0
|
|
|
|
Content-Type: application/json
|
|
|
|
{
|
|
"error": "Quota exceeded for <project-id>. Only <count> <resource>s
|
|
are allowed"
|
|
}
|
|
|
|
* Class Quotas
|
|
|
|
Class level quotas are not addressed in this spec. Need another spec to cover the
|
|
data model impact and REST API for associated CRUD operations.
|
|
|
|
|
|
Security impact
|
|
---------------
|
|
|
|
None
|
|
|
|
Notifications & Audit Impact
|
|
----------------------------
|
|
|
|
None
|
|
|
|
Other end user impact
|
|
---------------------
|
|
|
|
The Barbican client (python-barbicanclient) has to be enhanced to consume
|
|
the Quota REST API mentioned. The following scenarios should be supported.
|
|
|
|
Quota commands that a regular non-admin barbican user can make:
|
|
|
|
* List all quotas
|
|
|
|
barbican quota show
|
|
|
|
|
|
Quota commands that only a barbican admin can make
|
|
|
|
* List the default quotas applicable to all new projects
|
|
|
|
barbican quota show
|
|
|
|
* List quotas for a specific project
|
|
|
|
barbican quota show --project_id <project>
|
|
|
|
* Update quotas for a specific project
|
|
|
|
barbican quota update --project_id <project> --secrets 50 --orders 10
|
|
|
|
* Delete per-project quotas for a project
|
|
|
|
barbican quota delete --project_id <project>
|
|
|
|
|
|
|
|
Performance Impact
|
|
------------------
|
|
|
|
TBD
|
|
|
|
Other deployer impact
|
|
---------------------
|
|
|
|
The new data models introduced will be added by a new Alembic version file.
|
|
If automatic migration is turned OFF, the db migration tool has to be run
|
|
manually to effect the changes.
|
|
|
|
Developer impact
|
|
----------------
|
|
|
|
Developers integrating with Barbican API/client now need to handle the case
|
|
where the server could return a quota violation error
|
|
|
|
Implementation
|
|
==============
|
|
|
|
Assignee(s)
|
|
-----------
|
|
|
|
Dave McCowan (dave-mccowan) will be leading the implementation of the code.
|
|
|
|
Primary assignee:
|
|
<dave-mccowan>
|
|
|
|
Other assignees:
|
|
|
|
Work Items
|
|
----------
|
|
|
|
* Quota db provider source code
|
|
* Data model additions
|
|
* Alembic migration version script
|
|
* Updated default config file with quota section
|
|
* python-barbicanclient enhancements to support quota operations
|
|
* New unit tests to test quota related source changes
|
|
* Update existing resource unit tests to handle quota violation errors
|
|
* Functional tests
|
|
|
|
|
|
Dependencies
|
|
============
|
|
|
|
TBD
|
|
|
|
Testing
|
|
=======
|
|
|
|
New unit tests, functional tests, and tempest tests need to be added. Details TBD
|
|
|
|
|
|
Documentation Impact
|
|
====================
|
|
|
|
* A new section about Quotas has to be documented
|
|
* Existing resource API documentation needs to be updated with quota violation
|
|
specific errors
|
|
|
|
|
|
References
|
|
==========
|
|
|
|
TBD
|