CountableResource: try count/get functions for all plugins
It's of no guarantee that core plugin implements counter/getter function
for a CountableResource. Instead of just trying core plugin, try every
plugin registered in the directory.
To retain backwards compatibility, we also make sure that core plugin is
checked first.
Change-Id: I5245e217e1f44281f85febbdfaf873321253dc5d
Closes-Bug: #1714769
(cherry picked from commit 07bfe6adb9
)
This commit is contained in:
parent
671cbad9b9
commit
8d1b5bda38
@ -93,6 +93,8 @@ class DbQuotaDriver(object):
|
||||
used = resource.count_used(context, tenant_id,
|
||||
resync_usage=False)
|
||||
else:
|
||||
# NOTE(ihrachys) .count won't use the plugin we pass, but we
|
||||
# pass it regardless to keep the quota driver API intact
|
||||
plugins = directory.get_plugins()
|
||||
plugin = plugins.get(key, plugins[constants.CORE])
|
||||
used = resource.count(context, plugin, tenant_id)
|
||||
|
@ -12,6 +12,8 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from neutron_lib.plugins import constants
|
||||
from neutron_lib.plugins import directory
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log
|
||||
from oslo_utils import excutils
|
||||
@ -24,20 +26,32 @@ from neutron.db.quota import api as quota_api
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
def _count_resource(context, plugin, collection_name, tenant_id):
|
||||
def _count_resource(context, collection_name, tenant_id):
|
||||
count_getter_name = "get_%s_count" % collection_name
|
||||
getter_name = "get_%s" % collection_name
|
||||
|
||||
# Some plugins support a count method for particular resources,
|
||||
# using a DB's optimized counting features. We try to use that one
|
||||
# if present. Otherwise just use regular getter to retrieve all objects
|
||||
# and count in python, allowing older plugins to still be supported
|
||||
try:
|
||||
obj_count_getter = getattr(plugin, count_getter_name)
|
||||
return obj_count_getter(context, filters={'tenant_id': [tenant_id]})
|
||||
except (NotImplementedError, AttributeError):
|
||||
obj_getter = getattr(plugin, "get_%s" % collection_name)
|
||||
obj_list = obj_getter(context, filters={'tenant_id': [tenant_id]})
|
||||
return len(obj_list) if obj_list else 0
|
||||
plugins = directory.get_plugins()
|
||||
for pname in sorted(plugins,
|
||||
# inspect core plugin first
|
||||
key=lambda n: n != constants.CORE):
|
||||
# Some plugins support a count method for particular resources, using a
|
||||
# DB's optimized counting features. We try to use that one if present.
|
||||
# Otherwise just use regular getter to retrieve all objects and count
|
||||
# in python, allowing older plugins to still be supported
|
||||
try:
|
||||
obj_count_getter = getattr(plugins[pname], count_getter_name)
|
||||
return obj_count_getter(
|
||||
context, filters={'tenant_id': [tenant_id]})
|
||||
except (NotImplementedError, AttributeError):
|
||||
try:
|
||||
obj_getter = getattr(plugins[pname], getter_name)
|
||||
obj_list = obj_getter(
|
||||
context, filters={'tenant_id': [tenant_id]})
|
||||
return len(obj_list) if obj_list else 0
|
||||
except (NotImplementedError, AttributeError):
|
||||
pass
|
||||
raise NotImplementedError(
|
||||
'No plugins that support counting %s found.' % collection_name)
|
||||
|
||||
|
||||
class BaseResource(object):
|
||||
@ -129,7 +143,8 @@ class CountableResource(BaseResource):
|
||||
self._count_func = count
|
||||
|
||||
def count(self, context, plugin, tenant_id, **kwargs):
|
||||
return self._count_func(context, plugin, self.plural_name, tenant_id)
|
||||
# NOTE(ihrachys) _count_resource doesn't receive plugin
|
||||
return self._count_func(context, self.plural_name, tenant_id)
|
||||
|
||||
|
||||
class TrackedResource(BaseResource):
|
||||
|
@ -29,7 +29,7 @@ from neutron.tests.unit import testlib_api
|
||||
DB_PLUGIN_KLASS = 'neutron.db.db_base_plugin_v2.NeutronDbPluginV2'
|
||||
|
||||
|
||||
def _count_resource(context, plugin, resource, tenant_id):
|
||||
def _count_resource(context, resource, tenant_id):
|
||||
"""A fake counting function to determine current used counts"""
|
||||
if resource[-1] == 's':
|
||||
resource = resource[:-1]
|
||||
|
@ -14,6 +14,8 @@
|
||||
|
||||
import mock
|
||||
from neutron_lib import context
|
||||
from neutron_lib.plugins import constants
|
||||
from neutron_lib.plugins import directory
|
||||
from oslo_config import cfg
|
||||
from oslo_utils import uuidutils
|
||||
import testtools
|
||||
@ -325,3 +327,45 @@ class TestTrackedResource(testlib_api.SqlTestCase):
|
||||
self.assertNotIn(self.tenant_id, res._out_of_sync_tenants)
|
||||
mock_set_quota_usage.assert_called_once_with(
|
||||
self.context, self.resource, self.tenant_id, in_use=2)
|
||||
|
||||
|
||||
class Test_CountResource(base.BaseTestCase):
|
||||
|
||||
def test_all_plugins_checked(self):
|
||||
plugin1 = mock.Mock()
|
||||
plugin2 = mock.Mock()
|
||||
plugins = {'plugin1': plugin1, 'plugin2': plugin2}
|
||||
|
||||
for name, plugin in plugins.items():
|
||||
plugin.get_floatingips_count.side_effect = NotImplementedError
|
||||
plugin.get_floatingips.side_effect = NotImplementedError
|
||||
directory.add_plugin(name, plugin)
|
||||
|
||||
context = mock.Mock()
|
||||
collection_name = 'floatingips'
|
||||
tenant_id = 'fakeid'
|
||||
self.assertRaises(
|
||||
NotImplementedError,
|
||||
resource._count_resource, context, collection_name, tenant_id)
|
||||
|
||||
for plugin in plugins.values():
|
||||
for func in (plugin.get_floatingips_count, plugin.get_floatingips):
|
||||
func.assert_called_with(
|
||||
context, filters={'tenant_id': [tenant_id]})
|
||||
|
||||
def test_core_plugin_checked_first(self):
|
||||
plugin1 = mock.Mock()
|
||||
plugin2 = mock.Mock()
|
||||
|
||||
plugin1.get_floatingips_count.side_effect = NotImplementedError
|
||||
plugin1.get_floatingips.side_effect = NotImplementedError
|
||||
directory.add_plugin('plugin1', plugin1)
|
||||
|
||||
plugin2.get_floatingips_count.return_value = 10
|
||||
directory.add_plugin(constants.CORE, plugin2)
|
||||
|
||||
context = mock.Mock()
|
||||
collection_name = 'floatingips'
|
||||
tenant_id = 'fakeid'
|
||||
self.assertEqual(
|
||||
10, resource._count_resource(context, collection_name, tenant_id))
|
||||
|
Loading…
Reference in New Issue
Block a user