neutron/neutron/extensions/quotasv2.py

221 lines
8.2 KiB
Python

# Copyright 2011 OpenStack Foundation.
# All Rights Reserved.
#
# 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.
import warnings
from neutron_lib.api import converters
from neutron_lib.api import extensions as api_extensions
from neutron_lib.api import faults
from neutron_lib.db import constants as const
from neutron_lib import exceptions
from neutron_lib.plugins import directory
from oslo_config import cfg
from oslo_log import log as logging
from oslo_utils import importutils
import webob
from neutron._i18n import _
from neutron.api import extensions
from neutron.api.v2 import base
from neutron.api.v2 import resource
from neutron import policy
from neutron import quota
from neutron.quota import resource_registry
from neutron import wsgi
DEFAULT_QUOTAS_ACTION = 'default'
RESOURCE_NAME = 'quota'
RESOURCE_COLLECTION = RESOURCE_NAME + "s"
QUOTAS = quota.QUOTAS
DB_QUOTA_DRIVER = cfg.CONF.QUOTAS.quota_driver
EXTENDED_ATTRIBUTES_2_0 = {
RESOURCE_COLLECTION: {}
}
LOG = logging.getLogger(__name__)
def validate_policy(context, policy_name):
policy.init()
policy.enforce(context,
policy_name,
target={'project_id': context.project_id},
plugin=None)
class QuotaSetsController(wsgi.Controller):
def __init__(self, plugin):
self._resource_name = RESOURCE_NAME
self._plugin = plugin
self._driver = importutils.import_class(cfg.CONF.QUOTAS.quota_driver)()
self._update_extended_attributes = True
def _update_attributes(self):
for quota_resource in resource_registry.get_all_resources().keys():
attr_dict = EXTENDED_ATTRIBUTES_2_0[RESOURCE_COLLECTION]
attr_dict[quota_resource] = {
'allow_post': False,
'allow_put': True,
'convert_to': converters.convert_to_int,
'validate': {'type:range': [-1, const.DB_INTEGER_MAX_VALUE]},
'is_visible': True}
self._update_extended_attributes = False
def _get_quotas(self, request, project_id):
return self._driver.get_project_quotas(
request.context,
resource_registry.get_all_resources(),
project_id)
def default(self, request, id):
context = request.context
if id != context.project_id:
validate_policy(context, "get_quota")
return {self._resource_name: self._driver.get_default_quotas(
context=context,
resources=resource_registry.get_all_resources(),
project_id=id)}
def create(self, request, body=None):
msg = _('POST requests are not supported on this resource.')
raise webob.exc.HTTPNotImplemented(msg)
def index(self, request):
context = request.context
validate_policy(context, "get_quota")
return {self._resource_name + "s":
self._driver.get_all_quotas(
context, resource_registry.get_all_resources())}
def tenant(self, request):
"""Retrieve the project info in context."""
warnings.warn(
'"tenant" Quota API method is deprecated, use "project" instead')
return self._project(request, 'tenant')
def project(self, request):
"""Retrieve the project info in context."""
return self._project(request, 'project')
@staticmethod
def _project(request, key):
"""Retrieve the project info in context."""
context = request.context
if not context.project_id:
raise exceptions.QuotaMissingTenant()
return {key: {key + '_id': context.project_id}}
def show(self, request, id):
if id != request.context.project_id:
validate_policy(request.context, "get_quota")
return {self._resource_name: self._get_quotas(request, id)}
def delete(self, request, id):
validate_policy(request.context, "delete_quota")
self._driver.delete_project_quota(request.context, id)
def update(self, request, id, body=None):
validate_policy(request.context, "update_quota")
force = body[self._resource_name].pop('force', None)
check_limit = body[self._resource_name].pop('check_limit', None)
# NOTE(ralonsoh): these warning messages will be removed once
# LP#1953170 is completed and Neutron quota engine accepts "--force" or
# nothing (by default, Neutron quota engine will check the resource
# usage before setting the quota limit).
if force is None and check_limit is None:
warnings.warn('Neutron quota engine will require "--force" '
'parameter to set a quota limit without checking '
'the resource usage.')
elif check_limit:
warnings.warn('"--check-limit" parameter will not be needed in '
'Z+. By default, Neutron quota engine will check '
'the resource usage before setting a new quota '
'limit. Use "--force" to skip this check.')
if self._update_extended_attributes:
self._update_attributes()
try:
body = base.Controller.prepare_request_body(
request.context, body, False, self._resource_name,
EXTENDED_ATTRIBUTES_2_0[RESOURCE_COLLECTION])
except Exception as e:
LOG.warning(
"An exception happened while processing the request "
"body. The exception message is [%s].", e)
raise e
if check_limit:
resources = resource_registry.get_all_resources()
for resource_name, limit in body[self._resource_name].items():
resource_usage = self._driver.get_resource_usage(
request.context, id, resources, resource_name)
if resource_usage > limit:
msg = ('Quota limit %(limit)s for %(resource)s must be '
'greater than or equal to already used '
'%(resource_usage)s' %
{'limit': limit, 'resource': resource_name,
'resource_usage': resource_usage})
raise webob.exc.HTTPBadRequest(msg)
for key, value in body[self._resource_name].items():
self._driver.update_quota_limit(request.context, id, key, value)
return {self._resource_name: self._get_quotas(request, id)}
class Quotasv2(api_extensions.ExtensionDescriptor):
"""Quotas management support."""
extensions.register_custom_supported_check(
RESOURCE_COLLECTION, lambda: True, plugin_agnostic=True)
@classmethod
def get_name(cls):
return "Quota management support"
@classmethod
def get_alias(cls):
return RESOURCE_COLLECTION
@classmethod
def get_description(cls):
description = 'Expose functions for quotas management'
if cfg.CONF.QUOTAS.quota_driver == DB_QUOTA_DRIVER:
description += ' per project'
return description
@classmethod
def get_updated(cls):
return "2012-07-29T10:00:00-00:00"
@classmethod
def get_resources(cls):
"""Returns Ext Resources."""
controller = resource.Resource(
QuotaSetsController(directory.get_plugin()),
faults=faults.FAULT_MAP)
return [extensions.ResourceExtension(
Quotasv2.get_alias(),
controller,
member_actions={DEFAULT_QUOTAS_ACTION: 'GET'},
collection_actions={'tenant': 'GET',
'project': 'GET'})]
def get_extended_resources(self, version):
if version == "2.0":
return EXTENDED_ATTRIBUTES_2_0
else:
return {}