152 lines
5.8 KiB
Python
152 lines
5.8 KiB
Python
# Copyright (c) 2015 Taturiello Consulting, Meh.
|
|
# 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.
|
|
|
|
from neutron_lib.api import converters
|
|
from neutron_lib import exceptions as n_exc
|
|
from oslo_config import cfg
|
|
from oslo_log import log
|
|
from oslo_utils import importutils
|
|
import pecan
|
|
from pecan import request
|
|
from pecan import response
|
|
|
|
from neutron._i18n import _
|
|
from neutron.api.v2 import attributes
|
|
from neutron.common import constants
|
|
from neutron.pecan_wsgi.controllers import utils
|
|
from neutron.quota import resource_registry
|
|
|
|
LOG = log.getLogger(__name__)
|
|
RESOURCE_NAME = "quota"
|
|
TENANT_ID_ATTR = {'tenant_id':
|
|
{'allow_post': False,
|
|
'allow_put': False,
|
|
'required_by_policy': True,
|
|
'validate': {'type:string': attributes.TENANT_ID_MAX_LEN},
|
|
'is_visible': True}}
|
|
|
|
|
|
class QuotasController(utils.NeutronPecanController):
|
|
|
|
def __init__(self):
|
|
self._driver = importutils.import_class(
|
|
cfg.CONF.QUOTAS.quota_driver
|
|
)
|
|
super(QuotasController, self).__init__(
|
|
"%ss" % RESOURCE_NAME, RESOURCE_NAME)
|
|
|
|
def _check_admin(self, context,
|
|
reason=_("Only admin can view or configure quota")):
|
|
if not context.is_admin:
|
|
raise n_exc.AdminRequired(reason=reason)
|
|
|
|
@utils.expose()
|
|
def _lookup(self, tenant_id, *remainder):
|
|
return QuotaController(self._driver, tenant_id), remainder
|
|
|
|
@utils.expose(generic=True)
|
|
def index(self):
|
|
neutron_context = request.context.get('neutron_context')
|
|
# FIXME(salv-orlando): There shouldn't be any need to do this explicit
|
|
# check. However some behaviours from the "old" extension have
|
|
# been temporarily carried over here
|
|
self._check_admin(neutron_context)
|
|
# TODO(salv-orlando): proper plurals management
|
|
return {self.collection:
|
|
self._driver.get_all_quotas(
|
|
neutron_context,
|
|
resource_registry.get_all_resources())}
|
|
|
|
@utils.when(index, method='POST')
|
|
@utils.when(index, method='PUT')
|
|
@utils.when(index, method='DELETE')
|
|
def not_supported(self):
|
|
pecan.abort(405)
|
|
|
|
|
|
class QuotaController(utils.NeutronPecanController):
|
|
|
|
def __init__(self, _driver, tenant_id):
|
|
self._driver = _driver
|
|
self._tenant_id = tenant_id
|
|
|
|
super(QuotaController, self).__init__(
|
|
"%ss" % RESOURCE_NAME, RESOURCE_NAME)
|
|
|
|
# Ensure limits for all registered resources are returned
|
|
attr_dict = attributes.RESOURCE_ATTRIBUTE_MAP[self.collection]
|
|
for quota_resource in resource_registry.get_all_resources().keys():
|
|
attr_dict[quota_resource] = {
|
|
'allow_post': False,
|
|
'allow_put': True,
|
|
'convert_to': converters.convert_to_int,
|
|
'validate': {
|
|
'type:range': [-1, constants.DB_INTEGER_MAX_VALUE]},
|
|
'is_visible': True}
|
|
# The quota resource must always declare a tenant_id attribute,
|
|
# otherwise the attribute will be stripped off when generating the
|
|
# response
|
|
attr_dict.update(TENANT_ID_ATTR)
|
|
|
|
@utils.expose(generic=True)
|
|
def index(self):
|
|
return get_tenant_quotas(self._tenant_id, self._driver)
|
|
|
|
@utils.when(index, method='PUT')
|
|
def put(self, *args, **kwargs):
|
|
neutron_context = request.context.get('neutron_context')
|
|
# For put requests there's always going to be a single element
|
|
quota_data = request.context['resources'][0]
|
|
for key, value in quota_data.items():
|
|
self._driver.update_quota_limit(
|
|
neutron_context, self._tenant_id, key, value)
|
|
return get_tenant_quotas(self._tenant_id, self._driver)
|
|
|
|
@utils.when(index, method='DELETE')
|
|
def delete(self):
|
|
neutron_context = request.context.get('neutron_context')
|
|
self._driver.delete_tenant_quota(neutron_context,
|
|
self._tenant_id)
|
|
response.status = 204
|
|
|
|
@utils.when(index, method='POST')
|
|
def not_supported(self):
|
|
pecan.abort(405)
|
|
|
|
|
|
def get_tenant_quotas(tenant_id, driver=None):
|
|
if not driver:
|
|
driver = importutils.import_class(cfg.CONF.QUOTAS.quota_driver)
|
|
|
|
neutron_context = request.context.get('neutron_context')
|
|
if tenant_id == 'tenant':
|
|
# NOTE(salv-orlando): Read the following before the code in order
|
|
# to avoid puking.
|
|
# There is a weird undocumented behaviour of the Neutron quota API
|
|
# as 'tenant' is used as an API action to return the identifier
|
|
# of the tenant in the request context. This is used exclusively
|
|
# for interaction with python-neutronclient and is a possibly
|
|
# unnecessary 'whoami' API endpoint. Pending resolution of this
|
|
# API issue, this controller will just treat the magic string
|
|
# 'tenant' (and only that string) and return the response expected
|
|
# by python-neutronclient
|
|
return {'tenant': {'tenant_id': neutron_context.tenant_id}}
|
|
tenant_quotas = driver.get_tenant_quotas(
|
|
neutron_context,
|
|
resource_registry.get_all_resources(),
|
|
tenant_id)
|
|
tenant_quotas['tenant_id'] = tenant_id
|
|
return {RESOURCE_NAME: tenant_quotas}
|