]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Add plural names for quota resources
authorSalvatore Orlando <salv.orlando@gmail.com>
Tue, 23 Jun 2015 11:54:28 +0000 (04:54 -0700)
committerSalvatore Orlando <salv.orlando@gmail.com>
Tue, 28 Jul 2015 18:55:04 +0000 (11:55 -0700)
Introduce a 'plural_name' attribute in the BaseResource
class in the neutron.quota.resource module. This will
enable to specify the plural name of a resource when it's
entered in the quota registry.

This will come handy for managing reservations and also
removes the need for passing the collection name when
counting a resource.

Also, the name of the 'resources' parameter for the
_count_resource routine has ben changed to collection_name,
as the previous one was misleading, since it led to believe
it should be used for a collection, whereas it is simply
the name of a collection.

Change-Id: I0b61ba1856e9552cc14574be28fec6c77fb8783c
Related-Blueprint: better-quotas

neutron/api/v2/base.py
neutron/quota/resource.py
neutron/quota/resource_registry.py
neutron/tests/unit/quota/test_resource.py

index a1841b8cb8df0c86c0c365864bc432d05730dae3..cd591b4f9eaf45246e0d573811fecef9659c0ca8 100644 (file)
@@ -431,8 +431,7 @@ class Controller(object):
             try:
                 tenant_id = item[self._resource]['tenant_id']
                 count = quota.QUOTAS.count(request.context, self._resource,
-                                           self._plugin, self._collection,
-                                           tenant_id)
+                                           self._plugin, tenant_id)
                 if bulk:
                     delta = deltas.get(tenant_id, 0) + 1
                     deltas[tenant_id] = delta
index 25bcba761bd61ff75c2f8340249dd48a56ddb7a4..d9a716a5eb8c2c7a38143546948b1151e6d8d9ec 100644 (file)
@@ -26,8 +26,8 @@ from neutron.i18n import _LE
 LOG = log.getLogger(__name__)
 
 
-def _count_resource(context, plugin, resources, tenant_id):
-    count_getter_name = "get_%s_count" % resources
+def _count_resource(context, plugin, collection_name, tenant_id):
+    count_getter_name = "get_%s_count" % collection_name
 
     # Some plugins support a count method for particular resources,
     # using a DB's optimized counting features. We try to use that one
@@ -38,7 +38,7 @@ def _count_resource(context, plugin, resources, tenant_id):
         meh = obj_count_getter(context, filters={'tenant_id': [tenant_id]})
         return meh
     except (NotImplementedError, AttributeError):
-        obj_getter = getattr(plugin, "get_%s" % resources)
+        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
 
@@ -46,14 +46,33 @@ def _count_resource(context, plugin, resources, tenant_id):
 class BaseResource(object):
     """Describe a single resource for quota checking."""
 
-    def __init__(self, name, flag):
+    def __init__(self, name, flag, plural_name=None):
         """Initializes a resource.
 
         :param name: The name of the resource, i.e., "instances".
         :param flag: The name of the flag or configuration option
+        :param plural_name: Plural form of the resource name. If not
+                            specified, it is generated automatically by
+                            appending an 's' to the resource name, unless
+                            it ends with a 'y'. In that case the last
+                            letter is removed, and 'ies' is appended.
+                            Dashes are always converted to underscores.
         """
 
         self.name = name
+        # If a plural name is not supplied, default to adding an 's' to
+        # the resource name, unless the resource name ends in 'y', in which
+        # case remove the 'y' and add 'ies'. Even if the code should not fiddle
+        # too much with English grammar, this is a rather common and easy to
+        # implement rule.
+        if plural_name:
+            self.plural_name = plural_name
+        elif self.name[-1] == 'y':
+            self.plural_name = "%sies" % self.name[:-1]
+        else:
+            self.plural_name = "%ss" % self.name
+        # always convert dashes to underscores
+        self.plural_name = self.plural_name.replace('-', '_')
         self.flag = flag
 
     @property
@@ -79,7 +98,7 @@ class BaseResource(object):
 class CountableResource(BaseResource):
     """Describe a resource where the counts are determined by a function."""
 
-    def __init__(self, name, count, flag=None):
+    def __init__(self, name, count, flag=None, plural_name=None):
         """Initializes a CountableResource.
 
         Countable resources are those resources which directly
@@ -100,16 +119,26 @@ class CountableResource(BaseResource):
         :param flag: The name of the flag or configuration option
                      which specifies the default value of the quota
                      for this resource.
+        :param plural_name: Plural form of the resource name. If not
+                            specified, it is generated automatically by
+                            appending an 's' to the resource name, unless
+                            it ends with a 'y'. In that case the last
+                            letter is removed, and 'ies' is appended.
+                            Dashes are always converted to underscores.
         """
 
-        super(CountableResource, self).__init__(name, flag=flag)
-        self.count = count
+        super(CountableResource, self).__init__(
+            name, flag=flag, plural_name=plural_name)
+        self._count_func = count
+
+    def count(self, context, plugin, tenant_id):
+        return self._count_func(context, plugin, self.plural_name, tenant_id)
 
 
 class TrackedResource(BaseResource):
     """Resource which keeps track of its usage data."""
 
-    def __init__(self, name, model_class, flag):
+    def __init__(self, name, model_class, flag, plural_name=None):
         """Initializes an instance for a given resource.
 
         TrackedResource are directly mapped to data model classes.
@@ -126,8 +155,16 @@ class TrackedResource(BaseResource):
         :param flag: The name of the flag or configuration option
                      which specifies the default value of the quota
                      for this resource.
+        :param plural_name: Plural form of the resource name. If not
+                            specified, it is generated automatically by
+                            appending an 's' to the resource name, unless
+                            it ends with a 'y'. In that case the last
+                            letter is removed, and 'ies' is appended.
+                            Dashes are always converted to underscores.
+
         """
-        super(TrackedResource, self).__init__(name, flag)
+        super(TrackedResource, self).__init__(
+            name, flag=flag, plural_name=plural_name)
         # Register events for addition/removal of records in the model class
         # As tenant_id is immutable for all Neutron objects there is no need
         # to register a listener for update events
@@ -198,8 +235,7 @@ class TrackedResource(BaseResource):
         # Update quota usage
         return self._resync(context, tenant_id, in_use)
 
-    def count(self, context, _plugin, _resources, tenant_id,
-              resync_usage=False):
+    def count(self, context, _plugin, tenant_id, resync_usage=False):
         """Return the current usage count for the resource."""
         # Load current usage data
         usage_info = quota_api.get_quota_usage_by_resource_and_tenant(
index a0dfeebe7dc5298616ba5ae247fb0185b3f9e1f1..d0263e8761412f8a917d1f417b753fc75f8a26dd 100644 (file)
@@ -27,8 +27,9 @@ def register_resource(resource):
     ResourceRegistry.get_instance().register_resource(resource)
 
 
-def register_resource_by_name(resource_name):
-    ResourceRegistry.get_instance().register_resource_by_name(resource_name)
+def register_resource_by_name(resource_name, plural_name=None):
+    ResourceRegistry.get_instance().register_resource_by_name(
+        resource_name, plural_name)
 
 
 def get_all_resources():
@@ -151,7 +152,7 @@ class ResourceRegistry(object):
     def __contains__(self, resource):
         return resource in self._resources
 
-    def _create_resource_instance(self, resource_name):
+    def _create_resource_instance(self, resource_name, plural_name):
         """Factory function for quota Resource.
 
         This routine returns a resource instance of the appropriate type
@@ -220,9 +221,11 @@ class ResourceRegistry(object):
         for res in resources:
             self.register_resource(res)
 
-    def register_resource_by_name(self, resource_name):
+    def register_resource_by_name(self, resource_name,
+                                  plural_name=None):
         """Register a resource by name."""
-        resource = self._create_resource_instance(resource_name)
+        resource = self._create_resource_instance(
+            resource_name, plural_name)
         self.register_resource(resource)
 
     def unregister_resources(self):
index d1e890cd080b1048eeaf12ac9640b768874b5ab4..7f668539807395afd9d41b5f979d568c4256cdca 100644 (file)
@@ -31,6 +31,33 @@ meh_quota_opts = [cfg.IntOpt(meh_quota_flag, default=99)]
 random.seed()
 
 
+class TestResource(base.DietTestCase):
+    """Unit tests for neutron.quota.resource.BaseResource"""
+
+    def test_create_resource_without_plural_name(self):
+        res = resource.BaseResource('foo', None)
+        self.assertEqual('foos', res.plural_name)
+        res = resource.BaseResource('foy', None)
+        self.assertEqual('foies', res.plural_name)
+
+    def test_create_resource_with_plural_name(self):
+        res = resource.BaseResource('foo', None,
+                                    plural_name='foopsies')
+        self.assertEqual('foopsies', res.plural_name)
+
+    def test_resource_default_value(self):
+        res = resource.BaseResource('foo', 'foo_quota')
+        with mock.patch('oslo_config.cfg.CONF') as mock_cfg:
+            mock_cfg.QUOTAS.foo_quota = 99
+            self.assertEqual(99, res.default)
+
+    def test_resource_negative_default_value(self):
+        res = resource.BaseResource('foo', 'foo_quota')
+        with mock.patch('oslo_config.cfg.CONF') as mock_cfg:
+            mock_cfg.QUOTAS.foo_quota = -99
+            self.assertEqual(-1, res.default)
+
+
 class TestTrackedResource(testlib_api.SqlTestCaseLight):
 
     def _add_data(self, tenant_id=None):
@@ -98,9 +125,7 @@ class TestTrackedResource(testlib_api.SqlTestCaseLight):
             self.context, self.resource, dirty=False)
         # Expect correct count to be returned anyway since the first call to
         # count() always resyncs with the db
-        self.assertEqual(2, res.count(self.context,
-                                      None, None,
-                                      self.tenant_id))
+        self.assertEqual(2, res.count(self.context, None, self.tenant_id))
 
     def _test_count(self):
         res = self._create_resource()
@@ -111,14 +136,14 @@ class TestTrackedResource(testlib_api.SqlTestCaseLight):
 
     def test_count_with_dirty_false(self):
         res = self._test_count()
-        res.count(self.context, None, None, self.tenant_id)
+        res.count(self.context, None, self.tenant_id)
         # At this stage count has been invoked, and the dirty flag should be
         # false. Another invocation of count should not query the model class
         set_quota = 'neutron.db.quota.api.set_quota_usage'
         with mock.patch(set_quota) as mock_set_quota:
             self.assertEqual(0, mock_set_quota.call_count)
             self.assertEqual(2, res.count(self.context,
-                                          None, None,
+                                          None,
                                           self.tenant_id))
 
     def test_count_with_dirty_true_resync(self):
@@ -126,7 +151,7 @@ class TestTrackedResource(testlib_api.SqlTestCaseLight):
         # Expect correct count to be returned, which also implies
         # set_quota_usage has been invoked with the correct parameters
         self.assertEqual(2, res.count(self.context,
-                                      None, None,
+                                      None,
                                       self.tenant_id,
                                       resync_usage=True))
 
@@ -137,7 +162,7 @@ class TestTrackedResource(testlib_api.SqlTestCaseLight):
             quota_api.set_quota_usage_dirty(self.context,
                                             self.resource,
                                             self.tenant_id)
-            res.count(self.context, None, None, self.tenant_id,
+            res.count(self.context, None, self.tenant_id,
                       resync_usage=True)
             mock_set_quota_usage.assert_called_once_with(
                 self.context, self.resource, self.tenant_id, in_use=2)
@@ -147,9 +172,7 @@ class TestTrackedResource(testlib_api.SqlTestCaseLight):
         self._add_data()
         # Invoke count without having usage info in DB - Expect correct
         # count to be returned
-        self.assertEqual(2, res.count(self.context,
-                                      None, None,
-                                      self.tenant_id))
+        self.assertEqual(2, res.count(self.context, None, self.tenant_id))
 
     def test_count_with_dirty_true_no_usage_info_calls_set_quota_usage(self):
         res = self._create_resource()
@@ -159,8 +182,7 @@ class TestTrackedResource(testlib_api.SqlTestCaseLight):
             quota_api.set_quota_usage_dirty(self.context,
                                             self.resource,
                                             self.tenant_id)
-            res.count(self.context, None, None, self.tenant_id,
-                      resync_usage=True)
+            res.count(self.context, None, self.tenant_id, resync_usage=True)
             mock_set_quota_usage.assert_called_once_with(
                 self.context, self.resource, self.tenant_id, in_use=2)