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
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)
@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
# 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):
: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):
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)
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']
-
- # 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()
+ tenant_default = dict((key, resource.default)
+ for key, resource in resources.items())
+
+ 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):
"""
+++ /dev/null
-# 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)
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: {}
}
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())
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()
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."""
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)}
# 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
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
# 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"
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
functionality using NVP.
"""
- supported_extension_aliases = ["provider"]
+ supported_extension_aliases = ["provider", "quotas"]
# Default controller cluster
default_cluster = None
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
# 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"
@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):
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
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
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',
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')
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
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),
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