Merge "Add testing of Neutron per tenant quotas API"
diff --git a/tempest/api/network/base.py b/tempest/api/network/base.py
index 142ad7d..8785e31 100644
--- a/tempest/api/network/base.py
+++ b/tempest/api/network/base.py
@@ -38,6 +38,11 @@
 
         tenant_network_mask_bits with the mask bits to be used to partition the
         block defined by tenant-network_cidr
+
+    Finally, it is assumed that the following option is defined in the
+    [service_available] section of etc/tempest.conf
+
+        neutron as True
     """
 
     @classmethod
diff --git a/tempest/api/network/test_quotas.py b/tempest/api/network/test_quotas.py
new file mode 100644
index 0000000..ba70f34
--- /dev/null
+++ b/tempest/api/network/test_quotas.py
@@ -0,0 +1,91 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 OpenStack, LLC
+# All Rights Reserved.
+#
+#    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.
+
+
+from tempest.api.network import base
+from tempest import clients
+from tempest.common.utils.data_utils import rand_name
+from tempest.test import attr
+
+
+class QuotasTest(base.BaseNetworkTest):
+
+    """
+    Tests the following operations in the Neutron API using the REST client for
+    Neutron:
+
+        list quotas for tenants who have non-default quota values
+        show quotas for a specified tenant
+        update quotas for a specified tenant
+        reset quotas to default values for a specified tenant
+
+    v2.0 of the API is assumed. It is also assumed that the following
+    option is defined in the [service_available] section of etc/tempest.conf:
+
+        neutron as True
+
+    Finally, it is assumed that the per-tenant quota extension API is
+    configured in /etc/neutron/neutron.conf as follows:
+
+        quota_driver = neutron.db.quota_db.DbQuotaDriver
+    """
+
+    @classmethod
+    def setUpClass(cls):
+        super(QuotasTest, cls).setUpClass()
+        admin_manager = clients.AdminManager()
+        cls.admin_client = admin_manager.network_client
+        cls.identity_admin_client = admin_manager.identity_client
+
+    @attr(type='gate')
+    def test_quotas(self):
+        # Add a tenant to conduct the test
+        test_tenant = rand_name('test_tenant_')
+        test_description = rand_name('desc_')
+        _, tenant = self.identity_admin_client.create_tenant(
+            name=test_tenant,
+            description=test_description)
+        tenant_id = tenant['id']
+        self.addCleanup(self.identity_admin_client.delete_tenant, tenant_id)
+        # Change quotas for tenant
+        new_quotas = {'network': 0, 'security_group': 0}
+        resp, quota_set = self.admin_client.update_quotas(tenant_id,
+                                                          **new_quotas)
+        self.assertEqual('200', resp['status'])
+        self.addCleanup(self.admin_client.reset_quotas, tenant_id)
+        self.assertEqual(0, quota_set['network'])
+        self.assertEqual(0, quota_set['security_group'])
+        # Confirm our tenant is listed among tenants with non default quotas
+        resp, non_default_quotas = self.admin_client.list_quotas()
+        self.assertEqual('200', resp['status'])
+        found = False
+        for qs in non_default_quotas:
+            if qs['tenant_id'] == tenant_id:
+                found = True
+        self.assertTrue(found)
+        # Confirm from APi quotas were changed as requested for tenant
+        resp, quota_set = self.admin_client.show_quotas(tenant_id)
+        self.assertEqual('200', resp['status'])
+        self.assertEqual(0, quota_set['network'])
+        self.assertEqual(0, quota_set['security_group'])
+        # Reset quotas to default and confirm
+        resp, body = self.admin_client.reset_quotas(tenant_id)
+        self.assertEqual('204', resp['status'])
+        resp, non_default_quotas = self.admin_client.list_quotas()
+        self.assertEqual('200', resp['status'])
+        for q in non_default_quotas:
+            self.assertNotEqual(tenant_id, q['tenant_id'])
diff --git a/tempest/services/network/json/network_client.py b/tempest/services/network/json/network_client.py
index 446a674..f96ed91 100644
--- a/tempest/services/network/json/network_client.py
+++ b/tempest/services/network/json/network_client.py
@@ -23,13 +23,11 @@
     Tempest REST client for Neutron. Uses v2 of the Neutron API, since the
     V1 API has been removed from the code base.
 
-    Implements the following operations for each one of the basic Neutron
+    Implements create, delete, list and show for the basic Neutron
     abstractions (networks, sub-networks and ports):
 
-    create
-    delete
-    list
-    show
+    It also implements list, show, update and reset for OpenStack Networking
+    quotas
     """
 
     def __init__(self, config, username, password, auth_url, tenant_name=None):
@@ -128,3 +126,28 @@
         resp, body = self.get(uri, self.headers)
         body = json.loads(body)
         return resp, body
+
+    def update_quotas(self, tenant_id, **kwargs):
+        put_body = {'quota': kwargs}
+        body = json.dumps(put_body)
+        uri = '%s/quotas/%s' % (self.uri_prefix, tenant_id)
+        resp, body = self.put(uri, body, self.headers)
+        body = json.loads(body)
+        return resp, body['quota']
+
+    def show_quotas(self, tenant_id):
+        uri = '%s/quotas/%s' % (self.uri_prefix, tenant_id)
+        resp, body = self.get(uri, self.headers)
+        body = json.loads(body)
+        return resp, body['quota']
+
+    def reset_quotas(self, tenant_id):
+        uri = '%s/quotas/%s' % (self.uri_prefix, tenant_id)
+        resp, body = self.delete(uri, self.headers)
+        return resp, body
+
+    def list_quotas(self):
+        uri = '%s/quotas' % (self.uri_prefix)
+        resp, body = self.get(uri, self.headers)
+        body = json.loads(body)
+        return resp, body['quotas']