221 lines
8.2 KiB
Python
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 {}
|