Merge "refactor QuotaV2 import to match to other exts"
This commit is contained in:
commit
e0ae6aa598
@ -36,27 +36,6 @@ from quantum import wsgi
|
||||
|
||||
LOG = logging.getLogger('quantum.api.extensions')
|
||||
|
||||
# Besides the supported_extension_aliases in plugin class,
|
||||
# we also support register enabled extensions here so that we
|
||||
# can load some mandatory files (such as db models) before initialize plugin
|
||||
ENABLED_EXTS = {
|
||||
'quantum.plugins.linuxbridge.lb_quantum_plugin.LinuxBridgePluginV2':
|
||||
{
|
||||
'ext_alias': ["quotas"],
|
||||
'ext_db_models': ['quantum.extensions._quotav2_model.Quota'],
|
||||
},
|
||||
'quantum.plugins.openvswitch.ovs_quantum_plugin.OVSQuantumPluginV2':
|
||||
{
|
||||
'ext_alias': ["quotas"],
|
||||
'ext_db_models': ['quantum.extensions._quotav2_model.Quota'],
|
||||
},
|
||||
'quantum.plugins.nicira.nicira_nvp_plugin.QuantumPlugin.NvpPluginV2':
|
||||
{
|
||||
'ext_alias': ["quotas"],
|
||||
'ext_db_models': ['quantum.extensions._quotav2_model.Quota'],
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class PluginInterface(object):
|
||||
__metaclass__ = ABCMeta
|
||||
@ -559,9 +538,6 @@ class PluginAwareExtensionManager(ExtensionManager):
|
||||
alias in plugin.supported_extension_aliases)
|
||||
for plugin in self.plugins.values())
|
||||
plugin_provider = cfg.CONF.core_plugin
|
||||
if not supports_extension and plugin_provider in ENABLED_EXTS:
|
||||
supports_extension = (alias in
|
||||
ENABLED_EXTS[plugin_provider]['ext_alias'])
|
||||
if not supports_extension:
|
||||
LOG.warn(_("extension %s not supported by any of loaded plugins"),
|
||||
alias)
|
||||
@ -581,11 +557,6 @@ class PluginAwareExtensionManager(ExtensionManager):
|
||||
@classmethod
|
||||
def get_instance(cls):
|
||||
if cls._instance is None:
|
||||
plugin_provider = cfg.CONF.core_plugin
|
||||
if plugin_provider in ENABLED_EXTS:
|
||||
for model in ENABLED_EXTS[plugin_provider]['ext_db_models']:
|
||||
LOG.debug('loading model %s', model)
|
||||
model_class = importutils.import_class(model)
|
||||
cls._instance = cls(get_extensions_path(),
|
||||
QuantumManager.get_service_plugins())
|
||||
return cls._instance
|
||||
|
@ -15,8 +15,22 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
from quantum.common import exceptions
|
||||
from quantum.extensions import _quotav2_model as quotav2_model
|
||||
from quantum.db import model_base
|
||||
from quantum.db import models_v2
|
||||
|
||||
|
||||
class Quota(model_base.BASEV2, models_v2.HasId):
|
||||
"""Represent a single quota override for a tenant.
|
||||
|
||||
If there is no row for a given tenant id and resource, then the
|
||||
default for the quota class is used.
|
||||
"""
|
||||
tenant_id = sa.Column(sa.String(255), index=True)
|
||||
resource = sa.Column(sa.String(255))
|
||||
limit = sa.Column(sa.Integer)
|
||||
|
||||
|
||||
class DbQuotaDriver(object):
|
||||
@ -38,17 +52,15 @@ class DbQuotaDriver(object):
|
||||
:return dict: from resource name to dict of name and limit
|
||||
"""
|
||||
|
||||
quotas = {}
|
||||
tenant_quotas = context.session.query(
|
||||
quotav2_model.Quota).filter_by(tenant_id=tenant_id).all()
|
||||
tenant_quotas_dict = {}
|
||||
for _quota in tenant_quotas:
|
||||
tenant_quotas_dict[_quota['resource']] = _quota['limit']
|
||||
for key, resource in resources.items():
|
||||
quotas[key] = dict(
|
||||
name=key,
|
||||
limit=tenant_quotas_dict.get(key, resource.default))
|
||||
return quotas
|
||||
# init with defaults
|
||||
tenant_quota = dict((key, resource.default)
|
||||
for key, resource in resources.items())
|
||||
|
||||
# update with tenant specific limits
|
||||
q_qry = context.session.query(Quota).filter_by(tenant_id=tenant_id)
|
||||
tenant_quota.update((q['resource'], q['limit']) for q in q_qry.all())
|
||||
|
||||
return tenant_quota
|
||||
|
||||
@staticmethod
|
||||
def delete_tenant_quota(context, tenant_id):
|
||||
@ -57,8 +69,8 @@ class DbQuotaDriver(object):
|
||||
Atfer deletion, this tenant will use default quota values in conf.
|
||||
"""
|
||||
with context.session.begin():
|
||||
tenant_quotas = context.session.query(
|
||||
quotav2_model.Quota).filter_by(tenant_id=tenant_id).all()
|
||||
tenant_quotas = context.session.query(Quota).filter_by(
|
||||
tenant_id=tenant_id).all()
|
||||
for quota in tenant_quotas:
|
||||
context.session.delete(quota)
|
||||
|
||||
@ -74,22 +86,38 @@ class DbQuotaDriver(object):
|
||||
resourcekey2: ...
|
||||
"""
|
||||
|
||||
_quotas = context.session.query(quotav2_model.Quota).all()
|
||||
quotas = {}
|
||||
tenant_quotas_dict = {}
|
||||
for _quota in _quotas:
|
||||
tenant_id = _quota['tenant_id']
|
||||
if tenant_id not in quotas:
|
||||
quotas[tenant_id] = {'tenant_id': tenant_id}
|
||||
tenant_quotas_dict = quotas[tenant_id]
|
||||
tenant_quotas_dict[_quota['resource']] = _quota['limit']
|
||||
tenant_default = dict((key, resource.default)
|
||||
for key, resource in resources.items())
|
||||
|
||||
# we complete the quotas according to input resources
|
||||
for tenant_quotas_dict in quotas.itervalues():
|
||||
for key, resource in resources.items():
|
||||
tenant_quotas_dict[key] = tenant_quotas_dict.get(
|
||||
key, resource.default)
|
||||
return quotas.itervalues()
|
||||
all_tenant_quotas = {}
|
||||
|
||||
for quota in context.session.query(Quota).all():
|
||||
tenant_id = quota['tenant_id']
|
||||
|
||||
# avoid setdefault() because only want to copy when actually req'd
|
||||
tenant_quota = all_tenant_quotas.get(tenant_id)
|
||||
if tenant_quota is None:
|
||||
tenant_quota = tenant_default.copy()
|
||||
tenant_quota['tenant_id'] = tenant_id
|
||||
all_tenant_quotas[tenant_id] = tenant_quota
|
||||
|
||||
tenant_quota[quota['resource']] = quota['limit']
|
||||
|
||||
return all_tenant_quotas.itervalues()
|
||||
|
||||
@staticmethod
|
||||
def update_quota_limit(context, tenant_id, resource, limit):
|
||||
with context.session.begin():
|
||||
tenant_quota = context.session.query(Quota).filter_by(
|
||||
tenant_id=tenant_id, resource=resource).first()
|
||||
|
||||
if tenant_quota:
|
||||
tenant_quota.update({'limit': limit})
|
||||
else:
|
||||
tenant_quota = Quota(tenant_id=tenant_id,
|
||||
resource=resource,
|
||||
limit=limit)
|
||||
context.session.add(tenant_quota)
|
||||
|
||||
def _get_quotas(self, context, tenant_id, resources, keys):
|
||||
"""
|
@ -1,30 +0,0 @@
|
||||
# Copyright (c) 2012 OpenStack, LLC.
|
||||
#
|
||||
# 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 sqlalchemy as sa
|
||||
|
||||
from quantum.db import model_base
|
||||
from quantum.db import models_v2
|
||||
|
||||
|
||||
class Quota(model_base.BASEV2, models_v2.HasId):
|
||||
"""Represent a single quota override for a tenant.
|
||||
|
||||
If there is no row for a given tenant id and resource, then the
|
||||
default for the quota class is used.
|
||||
"""
|
||||
tenant_id = sa.Column(sa.String(255), index=True)
|
||||
resource = sa.Column(sa.String(255))
|
||||
limit = sa.Column(sa.Integer)
|
@ -20,17 +20,16 @@ import webob
|
||||
from quantum.api import extensions
|
||||
from quantum.api.v2 import base
|
||||
from quantum.common import exceptions
|
||||
from quantum.extensions import _quotav2_driver as quotav2_driver
|
||||
from quantum.extensions import _quotav2_model as quotav2_model
|
||||
from quantum.manager import QuantumManager
|
||||
from quantum.openstack.common import cfg
|
||||
from quantum.openstack.common import importutils
|
||||
from quantum import quota
|
||||
from quantum import wsgi
|
||||
|
||||
RESOURCE_NAME = 'quota'
|
||||
RESOURCE_COLLECTION = RESOURCE_NAME + "s"
|
||||
QUOTAS = quota.QUOTAS
|
||||
DB_QUOTA_DRIVER = 'quantum.extensions._quotav2_driver.DbQuotaDriver'
|
||||
DB_QUOTA_DRIVER = 'quantum.db.quota_db.DbQuotaDriver'
|
||||
EXTENDED_ATTRIBUTES_2_0 = {
|
||||
RESOURCE_COLLECTION: {}
|
||||
}
|
||||
@ -48,6 +47,7 @@ class QuotaSetsController(wsgi.Controller):
|
||||
def __init__(self, plugin):
|
||||
self._resource_name = RESOURCE_NAME
|
||||
self._plugin = plugin
|
||||
self._driver = importutils.import_class(DB_QUOTA_DRIVER)
|
||||
|
||||
def _get_body(self, request):
|
||||
body = self._deserialize(request.body, request.get_content_type())
|
||||
@ -57,9 +57,8 @@ class QuotaSetsController(wsgi.Controller):
|
||||
return req_body
|
||||
|
||||
def _get_quotas(self, request, tenant_id):
|
||||
values = quotav2_driver.DbQuotaDriver.get_tenant_quotas(
|
||||
return self._driver.get_tenant_quotas(
|
||||
request.context, QUOTAS.resources, tenant_id)
|
||||
return dict((k, v['limit']) for k, v in values.items())
|
||||
|
||||
def create(self, request, body=None):
|
||||
raise NotImplementedError()
|
||||
@ -69,8 +68,7 @@ class QuotaSetsController(wsgi.Controller):
|
||||
if not context.is_admin:
|
||||
raise webob.exc.HTTPForbidden()
|
||||
return {self._resource_name + "s":
|
||||
quotav2_driver.DbQuotaDriver.get_all_quotas(
|
||||
context, QUOTAS.resources)}
|
||||
self._driver.get_all_quotas(context, QUOTAS.resources)}
|
||||
|
||||
def tenant(self, request):
|
||||
"""Retrieve the tenant info in context."""
|
||||
@ -93,37 +91,26 @@ class QuotaSetsController(wsgi.Controller):
|
||||
def _check_modification_delete_privilege(self, context, tenant_id):
|
||||
if not tenant_id:
|
||||
raise webob.exc.HTTPBadRequest('invalid tenant')
|
||||
if (not context.is_admin):
|
||||
if not context.is_admin:
|
||||
raise webob.exc.HTTPForbidden()
|
||||
return tenant_id
|
||||
|
||||
def delete(self, request, id):
|
||||
tenant_id = id
|
||||
tenant_id = self._check_modification_delete_privilege(request.context,
|
||||
tenant_id)
|
||||
quotav2_driver.DbQuotaDriver.delete_tenant_quota(request.context,
|
||||
tenant_id)
|
||||
id)
|
||||
self._driver.delete_tenant_quota(request.context, tenant_id)
|
||||
|
||||
def update(self, request, id):
|
||||
tenant_id = id
|
||||
tenant_id = self._check_modification_delete_privilege(request.context,
|
||||
tenant_id)
|
||||
id)
|
||||
req_body = self._get_body(request)
|
||||
for key in req_body[self._resource_name].keys():
|
||||
if key in QUOTAS.resources:
|
||||
value = int(req_body[self._resource_name][key])
|
||||
with request.context.session.begin():
|
||||
tenant_quotas = request.context.session.query(
|
||||
quotav2_model.Quota).filter_by(tenant_id=tenant_id,
|
||||
resource=key).all()
|
||||
if not tenant_quotas:
|
||||
quota = quotav2_model.Quota(tenant_id=tenant_id,
|
||||
resource=key,
|
||||
limit=value)
|
||||
request.context.session.add(quota)
|
||||
else:
|
||||
quota = tenant_quotas[0]
|
||||
quota.update({'limit': value})
|
||||
self._driver.update_quota_limit(request.context,
|
||||
tenant_id,
|
||||
key,
|
||||
value)
|
||||
return {self._resource_name: self._get_quotas(request, tenant_id)}
|
||||
|
||||
|
||||
|
@ -40,4 +40,4 @@ default_quota = -1
|
||||
|
||||
# default driver to use for quota checks
|
||||
# quota_driver = quantum.quota.ConfDriver
|
||||
quota_driver = quantum.extensions._quotav2_driver.DbQuotaDriver
|
||||
quota_driver = quantum.db.quota_db.DbQuotaDriver
|
||||
|
@ -25,6 +25,7 @@ from quantum.db import db_base_plugin_v2
|
||||
from quantum.db import dhcp_rpc_base
|
||||
from quantum.db import l3_db
|
||||
from quantum.db import l3_rpc_base
|
||||
from quantum.db import quota_db
|
||||
from quantum.extensions import portbindings
|
||||
from quantum.extensions import providernet as provider
|
||||
from quantum.openstack.common import cfg
|
||||
@ -156,7 +157,7 @@ class LinuxBridgePluginV2(db_base_plugin_v2.QuantumDbPluginV2,
|
||||
# is qualified by class
|
||||
__native_bulk_support = True
|
||||
|
||||
supported_extension_aliases = ["provider", "router", "binding"]
|
||||
supported_extension_aliases = ["provider", "router", "binding", "quotas"]
|
||||
|
||||
network_view = "extension:provider_network:view"
|
||||
network_set = "extension:provider_network:set"
|
||||
|
@ -43,6 +43,7 @@ from quantum.db import api as db
|
||||
from quantum.db import db_base_plugin_v2
|
||||
from quantum.db import dhcp_rpc_base
|
||||
from quantum.db import models_v2
|
||||
from quantum.db import quota_db
|
||||
from quantum.extensions import providernet as pnet
|
||||
from quantum.openstack.common import cfg
|
||||
from quantum.openstack.common import rpc
|
||||
@ -117,7 +118,7 @@ class NvpPluginV2(db_base_plugin_v2.QuantumDbPluginV2):
|
||||
functionality using NVP.
|
||||
"""
|
||||
|
||||
supported_extension_aliases = ["provider"]
|
||||
supported_extension_aliases = ["provider", "quotas"]
|
||||
# Default controller cluster
|
||||
default_cluster = None
|
||||
|
||||
|
@ -31,6 +31,7 @@ from quantum.db import db_base_plugin_v2
|
||||
from quantum.db import dhcp_rpc_base
|
||||
from quantum.db import l3_db
|
||||
from quantum.db import l3_rpc_base
|
||||
from quantum.db import quota_db
|
||||
from quantum.extensions import portbindings
|
||||
from quantum.extensions import providernet as provider
|
||||
from quantum.openstack.common import cfg
|
||||
@ -194,7 +195,7 @@ class OVSQuantumPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
|
||||
# bulk operations. Name mangling is used in order to ensure it
|
||||
# is qualified by class
|
||||
__native_bulk_support = True
|
||||
supported_extension_aliases = ["provider", "router", "binding"]
|
||||
supported_extension_aliases = ["provider", "router", "binding", "quotas"]
|
||||
|
||||
network_view = "extension:provider_network:view"
|
||||
network_set = "extension:provider_network:set"
|
||||
|
@ -139,10 +139,9 @@ class BaseResource(object):
|
||||
@property
|
||||
def default(self):
|
||||
"""Return the default value of the quota."""
|
||||
if hasattr(cfg.CONF.QUOTAS, self.flag):
|
||||
return cfg.CONF.QUOTAS[self.flag]
|
||||
else:
|
||||
return cfg.CONF.QUOTAS.default_quota
|
||||
return getattr(cfg.CONF.QUOTAS,
|
||||
self.flag,
|
||||
cfg.CONF.QUOTAS.default_quota)
|
||||
|
||||
|
||||
class CountableResource(BaseResource):
|
||||
|
@ -23,7 +23,7 @@ from quantum.common.test_lib import test_config
|
||||
from quantum import context
|
||||
from quantum.db import api as db
|
||||
from quantum.db import l3_db
|
||||
from quantum.extensions import _quotav2_model as quotav2_model
|
||||
from quantum.db import quota_db
|
||||
from quantum.manager import QuantumManager
|
||||
from quantum.openstack.common import cfg
|
||||
from quantum.plugins.cisco.common import cisco_constants as const
|
||||
|
@ -26,12 +26,6 @@ _get_path = test_api_v2._get_path
|
||||
class QuotaExtensionTestCase(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
if getattr(self, 'testflag', 1) == 1:
|
||||
self._setUp1()
|
||||
else:
|
||||
self._setUp2()
|
||||
|
||||
def _setUp1(self):
|
||||
db._ENGINE = None
|
||||
db._MAKER = None
|
||||
# Ensure 'stale' patched copies of the plugin are never returned
|
||||
@ -53,7 +47,7 @@ class QuotaExtensionTestCase(unittest.TestCase):
|
||||
cfg.CONF.set_override('core_plugin', TARGET_PLUGIN)
|
||||
cfg.CONF.set_override(
|
||||
'quota_driver',
|
||||
'quantum.extensions._quotav2_driver.DbQuotaDriver',
|
||||
'quantum.db.quota_db.DbQuotaDriver',
|
||||
group='QUOTAS')
|
||||
cfg.CONF.set_override(
|
||||
'quota_items',
|
||||
@ -62,6 +56,7 @@ class QuotaExtensionTestCase(unittest.TestCase):
|
||||
|
||||
self._plugin_patcher = mock.patch(TARGET_PLUGIN, autospec=True)
|
||||
self.plugin = self._plugin_patcher.start()
|
||||
self.plugin.return_value.supported_extension_aliases = ['quotas']
|
||||
# QUOTAS will regester the items in conf when starting
|
||||
# extra1 here is added later, so have to do it manually
|
||||
quota.QUOTAS.register_resource_by_name('extra1')
|
||||
@ -71,34 +66,6 @@ class QuotaExtensionTestCase(unittest.TestCase):
|
||||
ext_middleware = extensions.ExtensionMiddleware(app, ext_mgr=ext_mgr)
|
||||
self.api = webtest.TestApp(ext_middleware)
|
||||
|
||||
def _setUp2(self):
|
||||
db._ENGINE = None
|
||||
db._MAKER = None
|
||||
# Ensure 'stale' patched copies of the plugin are never returned
|
||||
manager.QuantumManager._instance = None
|
||||
|
||||
# Ensure existing ExtensionManager is not used
|
||||
extensions.PluginAwareExtensionManager._instance = None
|
||||
|
||||
# Save the global RESOURCE_ATTRIBUTE_MAP
|
||||
self.saved_attr_map = {}
|
||||
for resource, attrs in attributes.RESOURCE_ATTRIBUTE_MAP.iteritems():
|
||||
self.saved_attr_map[resource] = attrs.copy()
|
||||
|
||||
# Create the default configurations
|
||||
args = ['--config-file', test_extensions.etcdir('quantum.conf.test')]
|
||||
config.parse(args=args)
|
||||
|
||||
# Update the plugin and extensions path
|
||||
cfg.CONF.set_override('core_plugin', TARGET_PLUGIN)
|
||||
self._plugin_patcher = mock.patch(TARGET_PLUGIN, autospec=True)
|
||||
self.plugin = self._plugin_patcher.start()
|
||||
ext_mgr = extensions.PluginAwareExtensionManager.get_instance()
|
||||
l2network_db_v2.initialize()
|
||||
app = config.load_paste_app('extensions_test_app')
|
||||
ext_middleware = extensions.ExtensionMiddleware(app, ext_mgr=ext_mgr)
|
||||
self.api = webtest.TestApp(ext_middleware)
|
||||
|
||||
def tearDown(self):
|
||||
self._plugin_patcher.stop()
|
||||
self.api = None
|
||||
@ -114,7 +81,7 @@ class QuotaExtensionTestCase(unittest.TestCase):
|
||||
res = self.api.get(_get_path('quotas'))
|
||||
self.assertEqual(200, res.status_int)
|
||||
|
||||
def test_quotas_defaul_values(self):
|
||||
def test_quotas_default_values(self):
|
||||
tenant_id = 'tenant_id1'
|
||||
env = {'quantum.context': context.Context('', tenant_id)}
|
||||
res = self.api.get(_get_path('quotas', id=tenant_id),
|
||||
@ -181,10 +148,8 @@ class QuotaExtensionTestCase(unittest.TestCase):
|
||||
self.assertEqual(403, res.status_int)
|
||||
|
||||
def test_quotas_loaded_bad(self):
|
||||
self.testflag = 2
|
||||
try:
|
||||
res = self.api.get(_get_path('quotas'), expect_errors=True)
|
||||
self.assertEqual(404, res.status_int)
|
||||
except Exception:
|
||||
pass
|
||||
self.testflag = 1
|
||||
|
Loading…
Reference in New Issue
Block a user