Merge "glance v2 image sharing tests"
diff --git a/tempest/api/image/base.py b/tempest/api/image/base.py
index fcd895e..28ed5b6 100644
--- a/tempest/api/image/base.py
+++ b/tempest/api/image/base.py
@@ -18,7 +18,7 @@
from tempest import clients
from tempest.common import isolated_creds
-from tempest.common.utils.data_utils import rand_name
+from tempest.common.utils import data_utils
from tempest import exceptions
from tempest.openstack.common import log as logging
import tempest.test
@@ -63,7 +63,7 @@
@classmethod
def create_image(cls, **kwargs):
"""Wrapper that returns a test image."""
- name = rand_name(cls.__name__ + "-instance")
+ name = data_utils.rand_name(cls.__name__ + "-instance")
if 'name' in kwargs:
name = kwargs.pop('name')
@@ -127,3 +127,40 @@
if not cls.config.image_feature_enabled.api_v2:
msg = "Glance API v2 not supported"
raise cls.skipException(msg)
+
+
+class BaseV2MemeberImageTest(BaseImageTest):
+
+ @classmethod
+ def setUpClass(cls):
+ super(BaseV2MemeberImageTest, cls).setUpClass()
+ if cls.config.compute.allow_tenant_isolation:
+ creds = cls.isolated_creds.get_alt_creds()
+ username, tenant_name, password = creds
+ cls.os_alt = clients.Manager(username=username,
+ password=password,
+ tenant_name=tenant_name,
+ interface=cls._interface)
+ cls.alt_tenant_id = cls.isolated_creds.get_alt_tenant()['id']
+ else:
+ cls.os_alt = clients.AltManager()
+ alt_tenant_name = cls.os_alt.tenant_name
+ identity_client = cls._get_identity_admin_client()
+ cls.alt_tenant_id = identity_client.get_tenant_by_name(
+ alt_tenant_name)['id']
+ cls.os_img_client = cls.os.image_client_v2
+ cls.alt_img_client = cls.os_alt.image_client_v2
+
+ def _list_image_ids_as_alt(self):
+ _, image_list = self.alt_img_client.image_list()
+ image_ids = map(lambda x: x['id'], image_list)
+ return image_ids
+
+ def _create_image(self):
+ name = data_utils.rand_name('image')
+ resp, image = self.os_img_client.create_image(name,
+ container_format='bare',
+ disk_format='raw')
+ image_id = image['id']
+ self.addCleanup(self.os_img_client.delete_image, image_id)
+ return image_id
diff --git a/tempest/api/image/v2/test_images_member.py b/tempest/api/image/v2/test_images_member.py
new file mode 100644
index 0000000..954c79d
--- /dev/null
+++ b/tempest/api/image/v2/test_images_member.py
@@ -0,0 +1,55 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# 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.image import base
+from tempest.test import attr
+
+
+class ImagesMemberTest(base.BaseV2MemeberImageTest):
+ _interface = 'json'
+
+ @attr(type='gate')
+ def test_image_share_accept(self):
+ image_id = self._create_image()
+ resp, member = self.os_img_client.add_member(image_id,
+ self.alt_tenant_id)
+ self.assertEqual(member['member_id'], self.alt_tenant_id)
+ self.assertEqual(member['image_id'], image_id)
+ self.assertEqual(member['status'], 'pending')
+ self.assertNotIn(image_id, self._list_image_ids_as_alt())
+ self.alt_img_client.update_member_status(image_id,
+ self.alt_tenant_id,
+ 'accepted')
+ self.assertIn(image_id, self._list_image_ids_as_alt())
+ _, body = self.os_img_client.get_image_membership(image_id)
+ members = body['members']
+ member = members[0]
+ self.assertEqual(len(members), 1, str(members))
+ self.assertEqual(member['member_id'], self.alt_tenant_id)
+ self.assertEqual(member['image_id'], image_id)
+ self.assertEqual(member['status'], 'accepted')
+
+ @attr(type='gate')
+ def test_image_share_reject(self):
+ image_id = self._create_image()
+ resp, member = self.os_img_client.add_member(image_id,
+ self.alt_tenant_id)
+ self.assertEqual(member['member_id'], self.alt_tenant_id)
+ self.assertEqual(member['image_id'], image_id)
+ self.assertEqual(member['status'], 'pending')
+ self.assertNotIn(image_id, self._list_image_ids_as_alt())
+ self.alt_img_client.update_member_status(image_id,
+ self.alt_tenant_id,
+ 'rejected')
+ self.assertNotIn(image_id, self._list_image_ids_as_alt())
diff --git a/tempest/api/image/v2/test_images_member_negative.py b/tempest/api/image/v2/test_images_member_negative.py
new file mode 100644
index 0000000..3c17959
--- /dev/null
+++ b/tempest/api/image/v2/test_images_member_negative.py
@@ -0,0 +1,43 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# 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.image import base
+from tempest import exceptions
+from tempest.test import attr
+
+
+class ImagesMemberNegativeTest(base.BaseV2MemeberImageTest):
+ _interface = 'json'
+
+ @attr(type=['negative', 'gate'])
+ def test_image_share_invalid_status(self):
+ image_id = self._create_image()
+ resp, member = self.os_img_client.add_member(image_id,
+ self.alt_tenant_id)
+ self.assertEqual(member['status'], 'pending')
+ self.assertRaises(exceptions.BadRequest,
+ self.alt_img_client.update_member_status,
+ image_id, self.alt_tenant_id, 'notavalidstatus')
+
+ @attr(type=['negative', 'gate'])
+ def test_image_share_owner_cannot_accept(self):
+ image_id = self._create_image()
+ resp, member = self.os_img_client.add_member(image_id,
+ self.alt_tenant_id)
+ self.assertEqual(member['status'], 'pending')
+ self.assertNotIn(image_id, self._list_image_ids_as_alt())
+ self.assertRaises(exceptions.Unauthorized,
+ self.os_img_client.update_member_status,
+ image_id, self.alt_tenant_id, 'accepted')
+ self.assertNotIn(image_id, self._list_image_ids_as_alt())
diff --git a/tempest/services/image/v2/json/image_client.py b/tempest/services/image/v2/json/image_client.py
index 342a09c..3d37267 100644
--- a/tempest/services/image/v2/json/image_client.py
+++ b/tempest/services/image/v2/json/image_client.py
@@ -134,3 +134,27 @@
url = 'v2/images/%s/tags/%s' % (image_id, tag)
resp, _ = self.delete(url)
return resp
+
+ def get_image_membership(self, image_id):
+ url = 'v2/images/%s/members' % image_id
+ resp, body = self.get(url)
+ body = json.loads(body)
+ self.expected_success(200, resp)
+ return resp, body
+
+ def add_member(self, image_id, member_id):
+ url = 'v2/images/%s/members' % image_id
+ data = json.dumps({'member': member_id})
+ resp, body = self.post(url, data, self.headers)
+ body = json.loads(body)
+ self.expected_success(200, resp)
+ return resp, body
+
+ def update_member_status(self, image_id, member_id, status):
+ """Valid status are: ``pending``, ``accepted``, ``rejected``."""
+ url = 'v2/images/%s/members/%s' % (image_id, member_id)
+ data = json.dumps({'status': status})
+ resp, body = self.put(url, data, self.headers)
+ body = json.loads(body)
+ self.expected_success(200, resp)
+ return resp, body