Merge "refactor QuotaV2 import to match to other exts"

This commit is contained in:
Jenkins 2013-01-08 03:47:00 +00:00 committed by Gerrit Code Review
commit e0ae6aa598
11 changed files with 84 additions and 161 deletions

View File

@ -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

View File

@ -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):
"""

View File

@ -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)

View File

@ -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)}

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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"

View File

@ -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):

View File

@ -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

View File

@ -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