Add a QuotaClient and quota API tests

This change adds a QuotaClient for testing the Barbican quota API.
It adds a quota test suite and integrates the QuotaClient with the
plugin testing infrastructure.

Change-Id: I3249fb879f519da63129e0ec5117377a21094974
Depends-On: I813648476a3a7b0b0e416a524d4629dffe36c92e
Implements: bp tempest-plugin
diff --git a/barbican_tempest_plugin/plugin.py b/barbican_tempest_plugin/plugin.py
index a0f4ca4..2c13b24 100644
--- a/barbican_tempest_plugin/plugin.py
+++ b/barbican_tempest_plugin/plugin.py
@@ -45,6 +45,7 @@
                 'ConsumerClient',
                 'ContainerClient',
                 'OrderClient',
+                'QuotaClient',
                 'SecretClient',
                 'SecretMetadataClient'
             ],
diff --git a/barbican_tempest_plugin/services/key_manager/json/__init__.py b/barbican_tempest_plugin/services/key_manager/json/__init__.py
index b3ee41e..7bce46a 100644
--- a/barbican_tempest_plugin/services/key_manager/json/__init__.py
+++ b/barbican_tempest_plugin/services/key_manager/json/__init__.py
@@ -18,6 +18,8 @@
     import ContainerClient
 from barbican_tempest_plugin.services.key_manager.json.order_client \
     import OrderClient
+from barbican_tempest_plugin.services.key_manager.json.quota_client \
+    import QuotaClient
 from barbican_tempest_plugin.services.key_manager.json.secret_client \
     import SecretClient
 from barbican_tempest_plugin.services.key_manager.json.secret_metadata_client \
@@ -27,6 +29,7 @@
     'ConsumerClient',
     'ContainerClient',
     'OrderClient',
+    'QuotaClient',
     'SecretClient',
     'SecretMetadataClient'
 ]
diff --git a/barbican_tempest_plugin/services/key_manager/json/quota_client.py b/barbican_tempest_plugin/services/key_manager/json/quota_client.py
new file mode 100644
index 0000000..2f90546
--- /dev/null
+++ b/barbican_tempest_plugin/services/key_manager/json/quota_client.py
@@ -0,0 +1,63 @@
+# Copyright (c) 2017 Johns Hopkins University Applied Physics Laboratory
+#
+# 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 json
+
+from six.moves.urllib import parse as urllib
+
+from tempest import config
+from tempest.lib.common import rest_client
+
+CONF = config.CONF
+
+
+class QuotaClient(rest_client.RestClient):
+
+    def list_quotas(self, **kwargs):
+        uri = "v1/project-quotas"
+        if kwargs:
+            uri += "?%s" % urllib.urlencode(kwargs)
+
+        response, body = self.get(uri)
+        self.expected_success(200, response.status)
+        return json.loads(body)
+
+    def get_default_project_quota(self):
+        uri = "v1/quotas"
+
+        response, body = self.get(uri)
+        self.expected_success(200, response.status)
+        return json.loads(body)
+
+    def get_project_quota(self, project_id):
+        uri = "v1/project-quotas/%s" % project_id
+
+        response, body = self.get(uri)
+        self.expected_success(200, response.status)
+        return json.loads(body)
+
+    def create_project_quota(self, project_id, **kwargs):
+        uri = "v1/project-quotas/%s" % project_id
+
+        response, body = self.put(uri, json.dumps(kwargs))
+        self.expected_success(204, response.status)
+        return
+
+    def delete_project_quota(self, project_id):
+        uri = "v1/project-quotas/%s" % project_id
+
+        response, _ = self.delete(uri)
+        self.expected_success(204, response.status)
+        return
diff --git a/barbican_tempest_plugin/tests/api/base.py b/barbican_tempest_plugin/tests/api/base.py
index abf1e83..7256a10 100644
--- a/barbican_tempest_plugin/tests/api/base.py
+++ b/barbican_tempest_plugin/tests/api/base.py
@@ -23,7 +23,7 @@
 CONF = config.CONF
 
 # NOTE(dane-fichter): We need to track resource types for cleanup.
-RESOURCE_TYPES = ['container', 'order', 'secret']
+RESOURCE_TYPES = ['container', 'order', 'quota', 'secret']
 
 
 def _get_uuid(href):
@@ -45,6 +45,9 @@
                 secret_ref = order_metadata.get('secret_ref')
                 if secret_ref:
                     cls.created_objects['secret'].add(_get_uuid(secret_ref))
+                uuid = _get_uuid(resp['order_ref'])
+            if resource == 'quota':
+                uuid = _get_uuid(args[0])
             if resource == 'secret':
                 uuid = _get_uuid(resp['secret_ref'])
             cls.created_objects[resource].add(uuid)
@@ -57,7 +60,7 @@
     """Base class for all api tests."""
 
     # Why do I have to be an admin to create secrets? No idea...
-    credentials = ('admin', )
+    credentials = ('admin', ['service_admin', 'key-manager:service-admin'])
     client_manager = clients.Clients
     created_objects = {}
 
@@ -77,6 +80,9 @@
             service='key-manager'
         )
 
+        os = getattr(cls, 'os_roles_%s' % cls.credentials[1][0])
+        cls.quota_client = os.secret_v1.QuotaClient(service='key-manager')
+
     @classmethod
     def resource_setup(cls):
         super(BaseKeyManagerTest, cls).resource_setup()
@@ -90,6 +96,8 @@
                 cls.delete_container(container_uuid)
             for order_uuid in list(cls.created_objects['order']):
                 cls.delete_order(order_uuid)
+            for project_quota_uuid in list(cls.created_objects['quota']):
+                cls.delete_project_quota(project_quota_uuid)
             for secret_uuid in list(cls.created_objects['secret']):
                 cls.delete_secret(secret_uuid)
         finally:
@@ -120,6 +128,16 @@
         return cls.order_client.delete_order(uuid)
 
     @classmethod
+    @creates('quota')
+    def create_project_quota(cls, project_id, **kwargs):
+        return cls.quota_client.create_project_quota(project_id, **kwargs)
+
+    @classmethod
+    def delete_project_quota(cls, project_id):
+        cls.created_objects['quota'].remove(project_id)
+        return cls.quota_client.delete_project_quota(project_id)
+
+    @classmethod
     @creates('secret')
     def create_secret(cls, **kwargs):
         return cls.secret_client.create_secret(**kwargs)
diff --git a/barbican_tempest_plugin/tests/api/test_quotas.py b/barbican_tempest_plugin/tests/api/test_quotas.py
new file mode 100644
index 0000000..882fbb1
--- /dev/null
+++ b/barbican_tempest_plugin/tests/api/test_quotas.py
@@ -0,0 +1,79 @@
+# Copyright (c) 2016 Johns Hopkins University Applied Physics Laboratory
+#
+# 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 barbican_tempest_plugin.tests.api import base
+
+from tempest import config
+
+CONF = config.CONF
+
+
+class QuotasTest(base.BaseKeyManagerTest):
+    """Quotas API tests."""
+
+    def test_create_get_delete_quota(self):
+        # Verify the default quota settings
+        body = self.quota_client.get_default_project_quota()
+        quotas = body.get('quotas')
+        self.assertEqual(-1, quotas.get('secrets'))
+        self.assertEqual(-1, quotas.get('cas'))
+        self.assertEqual(-1, quotas.get('orders'))
+        self.assertEqual(-1, quotas.get('containers'))
+        self.assertEqual(-1, quotas.get('consumers'))
+
+        # Confirm that there are no quotas
+        body = self.quota_client.list_quotas()
+        self.assertEqual(0, body.get('total'), body)
+        self.assertEqual(0, len(body.get('project_quotas')), body)
+
+        # Create a quota set for the test project
+        self.create_project_quota(
+            self.quota_client.tenant_id,
+            project_quotas={
+                'secrets': 30,
+                'orders': 10,
+                'containers': 20
+            }
+        )
+
+        # Verify that the quotas can be found via generic listing.
+        body = self.quota_client.list_quotas()
+        self.assertEqual(1, body.get('total'), body)
+        self.assertEqual(1, len(body.get('project_quotas')), body)
+        project_quotas = body.get('project_quotas')[0]
+        self.assertEqual(
+            self.quota_client.tenant_id,
+            project_quotas.get('project_id'),
+            body
+        )
+        project_quotas = project_quotas.get('project_quotas')
+        self.assertEqual(30, project_quotas.get('secrets'), body)
+        self.assertEqual(10, project_quotas.get('orders'), body)
+        self.assertEqual(20, project_quotas.get('containers'), body)
+
+        # Verify that the quotas can be found via specific listing.
+        body = self.quota_client.get_project_quota(
+            self.quota_client.tenant_id
+        )
+        project_quotas = body.get('project_quotas')
+        self.assertEqual(30, project_quotas.get('secrets'), body)
+        self.assertEqual(10, project_quotas.get('orders'), body)
+        self.assertEqual(20, project_quotas.get('containers'), body)
+
+        # Delete the project quota and confirm that it got deleted
+        self.delete_project_quota(self.quota_client.tenant_id)
+
+        body = self.quota_client.list_quotas()
+        self.assertEqual(0, body.get('total'), body)
+        self.assertEqual(0, len(body.get('project_quotas')), body)