Merge "Prepare the Manager class for tempest.lib"
diff --git a/tempest/api/volume/admin/test_multi_backend.py b/tempest/api/volume/admin/test_multi_backend.py
index 5e1c20b..5615cf3 100644
--- a/tempest/api/volume/admin/test_multi_backend.py
+++ b/tempest/api/volume/admin/test_multi_backend.py
@@ -63,9 +63,8 @@
             extra_specs = {spec_key_with_prefix: backend_name_key}
         else:
             extra_specs = {spec_key_without_prefix: backend_name_key}
-        self.type = self.admin_volume_types_client.create_volume_type(
-            name=type_name, extra_specs=extra_specs)['volume_type']
-        self.volume_type_id_list.append(self.type['id'])
+        self.type = self.create_volume_type(name=type_name,
+                                            extra_specs=extra_specs)
 
         params = {self.name_field: vol_name, 'volume_type': type_name}
 
@@ -92,11 +91,6 @@
             cls.admin_volume_client.delete_volume(volume_id)
             cls.admin_volume_client.wait_for_resource_deletion(volume_id)
 
-        # volume types deletion
-        volume_type_id_list = getattr(cls, 'volume_type_id_list', [])
-        for volume_type_id in volume_type_id_list:
-            cls.admin_volume_types_client.delete_volume_type(volume_type_id)
-
         super(VolumeMultiBackendV2Test, cls).resource_cleanup()
 
     @test.idempotent_id('c1a41f3f-9dad-493e-9f09-3ff197d477cc')
diff --git a/tempest/api/volume/admin/test_qos.py b/tempest/api/volume/admin/test_qos.py
index 68e57f5..9402668 100644
--- a/tempest/api/volume/admin/test_qos.py
+++ b/tempest/api/volume/admin/test_qos.py
@@ -50,14 +50,6 @@
         list_qos = self.admin_volume_qos_client.list_qos()['qos_specs']
         self.assertNotIn(body, list_qos)
 
-    def _create_test_volume_type(self):
-        vol_type_name = utils.rand_name("volume-type")
-        vol_type = self.admin_volume_types_client.create_volume_type(
-            name=vol_type_name)['volume_type']
-        self.addCleanup(self.admin_volume_types_client.delete_volume_type,
-                        vol_type['id'])
-        return vol_type
-
     def _test_associate_qos(self, vol_type_id):
         self.admin_volume_qos_client.associate_qos(
             self.created_qos['id'], vol_type_id)
@@ -146,7 +138,7 @@
         # create a test volume-type
         vol_type = []
         for _ in range(0, 3):
-            vol_type.append(self._create_test_volume_type())
+            vol_type.append(self.create_volume_type())
 
         # associate the qos-specs with volume-types
         for i in range(0, 3):
diff --git a/tempest/api/volume/admin/test_volume_type_access.py b/tempest/api/volume/admin/test_volume_type_access.py
new file mode 100644
index 0000000..fac71a8
--- /dev/null
+++ b/tempest/api/volume/admin/test_volume_type_access.py
@@ -0,0 +1,94 @@
+# Copyright 2016 OpenStack Foundation
+# 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.
+
+import operator
+
+from tempest.api.volume import base
+from tempest.common import waiters
+from tempest.lib import exceptions as lib_exc
+from tempest import test
+
+
+class VolumeTypesAccessV2Test(base.BaseVolumeAdminTest):
+
+    credentials = ['primary', 'alt', 'admin']
+
+    @classmethod
+    def setup_clients(cls):
+        super(VolumeTypesAccessV2Test, cls).setup_clients()
+        cls.alt_client = cls.os_alt.volumes_client
+
+    @test.idempotent_id('d4dd0027-835f-4554-a6e5-50903fb79184')
+    def test_volume_type_access_add(self):
+        # Creating a NON public volume type
+        params = {'os-volume-type-access:is_public': False}
+        volume_type = self.create_volume_type(**params)
+
+        # Try creating a volume from volume type in primary tenant
+        self.assertRaises(lib_exc.NotFound, self.volumes_client.create_volume,
+                          volume_type=volume_type['id'])
+
+        # Adding volume type access for primary tenant
+        self.admin_volume_types_client.add_type_access(
+            volume_type['id'], project=self.volumes_client.tenant_id)
+        self.addCleanup(self.admin_volume_types_client.remove_type_access,
+                        volume_type['id'],
+                        project=self.volumes_client.tenant_id)
+
+        # Creating a volume from primary tenant
+        volume = self.volumes_client.create_volume(
+            volume_type=volume_type['id'])['volume']
+        self.addCleanup(self.delete_volume, self.volumes_client, volume['id'])
+        waiters.wait_for_volume_status(self.volumes_client, volume['id'],
+                                       'available')
+
+        # Validating the created volume is based on the volume type
+        self.assertEqual(volume_type['name'], volume['volume_type'])
+
+    @test.idempotent_id('5220eb28-a435-43ce-baaf-ed46f0e95159')
+    def test_volume_type_access_list(self):
+        # Creating a NON public volume type
+        params = {'os-volume-type-access:is_public': False}
+        volume_type = self.create_volume_type(**params)
+
+        # Adding volume type access for primary tenant
+        self.admin_volume_types_client.add_type_access(
+            volume_type['id'], project=self.volumes_client.tenant_id)
+        self.addCleanup(self.admin_volume_types_client.remove_type_access,
+                        volume_type['id'],
+                        project=self.volumes_client.tenant_id)
+
+        # Adding volume type access for alt tenant
+        self.admin_volume_types_client.add_type_access(
+            volume_type['id'], project=self.alt_client.tenant_id)
+        self.addCleanup(self.admin_volume_types_client.remove_type_access,
+                        volume_type['id'],
+                        project=self.alt_client.tenant_id)
+
+        # List tenant access for the given volume type
+        type_access_list = self.admin_volume_types_client.list_type_access(
+            volume_type['id'])['volume_type_access']
+        volume_type_ids = [
+            vol_type['volume_type_id'] for vol_type in type_access_list
+        ]
+
+        # Validating volume type available for only two tenants
+        self.assertEqual(2, volume_type_ids.count(volume_type['id']))
+
+        # Validating the permitted tenants are the expected tenants
+        self.assertIn(self.volumes_client.tenant_id,
+                      map(operator.itemgetter('project_id'), type_access_list))
+        self.assertIn(self.alt_client.tenant_id,
+                      map(operator.itemgetter('project_id'), type_access_list))
diff --git a/tempest/api/volume/admin/test_volume_types.py b/tempest/api/volume/admin/test_volume_types.py
index 9023037..27f6ccb 100644
--- a/tempest/api/volume/admin/test_volume_types.py
+++ b/tempest/api/volume/admin/test_volume_types.py
@@ -44,12 +44,10 @@
         # Create two volume_types
         for i in range(2):
             vol_type_name = data_utils.rand_name("volume-type")
-            vol_type = self.admin_volume_types_client.create_volume_type(
+            vol_type = self.create_volume_type(
                 name=vol_type_name,
-                extra_specs=extra_specs)['volume_type']
+                extra_specs=extra_specs)
             volume_types.append(vol_type)
-            self.addCleanup(self.admin_volume_types_client.delete_volume_type,
-                            vol_type['id'])
         params = {self.name_field: vol_name,
                   'volume_type': volume_types[0]['id']}
 
@@ -94,12 +92,10 @@
         vendor = CONF.volume.vendor_name
         extra_specs = {"storage_protocol": proto,
                        "vendor_name": vendor}
-        body = self.admin_volume_types_client.create_volume_type(
+        body = self.create_volume_type(
             name=name,
-            extra_specs=extra_specs)['volume_type']
+            extra_specs=extra_specs)
         self.assertIn('id', body)
-        self.addCleanup(self.admin_volume_types_client.delete_volume_type,
-                        body['id'])
         self.assertIn('name', body)
         self.assertEqual(body['name'], name,
                          "The created volume_type name is not equal "
@@ -124,11 +120,7 @@
         provider = "LuksEncryptor"
         control_location = "front-end"
         name = data_utils.rand_name("volume-type")
-        body = self.admin_volume_types_client.create_volume_type(
-            name=name)['volume_type']
-        self.addCleanup(self.admin_volume_types_client.delete_volume_type,
-                        body['id'])
-
+        body = self.create_volume_type(name=name)
         # Create encryption type
         encryption_type = \
             self.admin_volume_types_client.create_encryption_type(
diff --git a/tempest/api/volume/admin/test_volume_types_extra_specs.py b/tempest/api/volume/admin/test_volume_types_extra_specs.py
index b5c5d15..9e49b94 100644
--- a/tempest/api/volume/admin/test_volume_types_extra_specs.py
+++ b/tempest/api/volume/admin/test_volume_types_extra_specs.py
@@ -24,14 +24,7 @@
     def resource_setup(cls):
         super(VolumeTypesExtraSpecsV2Test, cls).resource_setup()
         vol_type_name = data_utils.rand_name('Volume-type')
-        cls.volume_type = \
-            cls.admin_volume_types_client.create_volume_type(
-                name=vol_type_name)['volume_type']
-
-    @classmethod
-    def resource_cleanup(cls):
-        cls.admin_volume_types_client.delete_volume_type(cls.volume_type['id'])
-        super(VolumeTypesExtraSpecsV2Test, cls).resource_cleanup()
+        cls.volume_type = cls.create_volume_type(name=vol_type_name)
 
     @test.idempotent_id('b42923e9-0452-4945-be5b-d362ae533e60')
     def test_volume_type_extra_specs_list(self):
diff --git a/tempest/api/volume/admin/test_volume_types_extra_specs_negative.py b/tempest/api/volume/admin/test_volume_types_extra_specs_negative.py
index c7c3e1a..2193aa6 100644
--- a/tempest/api/volume/admin/test_volume_types_extra_specs_negative.py
+++ b/tempest/api/volume/admin/test_volume_types_extra_specs_negative.py
@@ -26,14 +26,8 @@
         super(ExtraSpecsNegativeV2Test, cls).resource_setup()
         vol_type_name = data_utils.rand_name('Volume-type')
         cls.extra_specs = {"spec1": "val1"}
-        cls.volume_type = cls.admin_volume_types_client.create_volume_type(
-            name=vol_type_name,
-            extra_specs=cls.extra_specs)['volume_type']
-
-    @classmethod
-    def resource_cleanup(cls):
-        cls.admin_volume_types_client.delete_volume_type(cls.volume_type['id'])
-        super(ExtraSpecsNegativeV2Test, cls).resource_cleanup()
+        cls.volume_type = cls.create_volume_type(name=vol_type_name,
+                                                 extra_specs=cls.extra_specs)
 
     @test.idempotent_id('08961d20-5cbb-4910-ac0f-89ad6dbb2da1')
     def test_update_no_body(self):
diff --git a/tempest/api/volume/base.py b/tempest/api/volume/base.py
index 665036b..e9be529 100644
--- a/tempest/api/volume/base.py
+++ b/tempest/api/volume/base.py
@@ -214,11 +214,13 @@
         super(BaseVolumeAdminTest, cls).resource_setup()
 
         cls.qos_specs = []
+        cls.volume_types = []
 
     @classmethod
     def resource_cleanup(cls):
         cls.clear_qos_specs()
         super(BaseVolumeAdminTest, cls).resource_cleanup()
+        cls.clear_volume_types()
 
     @classmethod
     def create_test_qos_specs(cls, name=None, consumer=None, **kwargs):
@@ -231,6 +233,15 @@
         return qos_specs
 
     @classmethod
+    def create_volume_type(cls, name=None, **kwargs):
+        """Create a test volume-type"""
+        name = name or data_utils.rand_name('volume-type')
+        volume_type = cls.admin_volume_types_client.create_volume_type(
+            name=name, **kwargs)['volume_type']
+        cls.volume_types.append(volume_type['id'])
+        return volume_type
+
+    @classmethod
     def clear_qos_specs(cls):
         for qos_id in cls.qos_specs:
             test_utils.call_and_ignore_notfound_exc(
@@ -239,3 +250,17 @@
         for qos_id in cls.qos_specs:
             test_utils.call_and_ignore_notfound_exc(
                 cls.admin_volume_qos_client.wait_for_resource_deletion, qos_id)
+
+    @classmethod
+    def clear_volume_types(cls):
+        for vol_type in cls.volume_types:
+            test_utils.call_and_ignore_notfound_exc(
+                cls.admin_volume_types_client.delete_volume_type, vol_type)
+
+        for vol_type in cls.volume_types:
+            # Resource dictionary uses for is_resource_deleted method,
+            # to distinguish between volume-type to encryption-type.
+            resource = {'id': vol_type, 'type': 'volume-type'}
+            test_utils.call_and_ignore_notfound_exc(
+                cls.admin_volume_types_client.wait_for_resource_deletion,
+                resource)
diff --git a/tempest/services/volume/base/admin/base_types_client.py b/tempest/services/volume/base/admin/base_types_client.py
index 95ddff6..e4d9014 100644
--- a/tempest/services/volume/base/admin/base_types_client.py
+++ b/tempest/services/volume/base/admin/base_types_client.py
@@ -179,3 +179,37 @@
             "/types/%s/encryption/provider" % str(vol_type_id))
         self.expected_success(202, resp.status)
         return rest_client.ResponseBody(resp, body)
+
+    def add_type_access(self, volume_type_id, **kwargs):
+        """Adds volume type access for the given project.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-blockstorage-v2.html
+                              #createVolumeTypeAccessExt
+        """
+        post_body = json.dumps({'addProjectAccess': kwargs})
+        url = 'types/%s/action' % (volume_type_id)
+        resp, body = self.post(url, post_body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def remove_type_access(self, volume_type_id, **kwargs):
+        """Removes volume type access for the given project.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-blockstorage-v2.html
+                              #removeVolumeTypeAccessExt
+        """
+        post_body = json.dumps({'removeProjectAccess': kwargs})
+        url = 'types/%s/action' % (volume_type_id)
+        resp, body = self.post(url, post_body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_type_access(self, volume_type_id):
+        """Print access information about the given volume type."""
+        url = 'types/%s/os-volume-type-access' % (volume_type_id)
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/test-requirements.txt b/test-requirements.txt
index 763f0ba..a5b0d29 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -1,7 +1,7 @@
 # The order of packages is significant, because pip processes them in the order
 # of appearance. Changing the order has an impact on the overall integration
 # process, which may cause wedges in the gate later.
-hacking<0.11,>=0.10.0
+hacking<0.12,>=0.11.0 # Apache-2.0
 # needed for doc build
 sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2 # BSD
 python-subunit>=0.0.18 # Apache-2.0/BSD