Merge "CountableResource: try count/get functions for all plugins" into stable/pike

This commit is contained in:
Jenkins 2017-09-13 22:09:54 +00:00 committed by Gerrit Code Review
commit 1f474527f5
4 changed files with 75 additions and 14 deletions

View File

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

View File

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

View File

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

View File

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