Merge "Add base classes for image tests."
diff --git a/tempest/services/image/v1/json/image_client.py b/tempest/services/image/v1/json/image_client.py
index 45e93e2..77c9cd2 100644
--- a/tempest/services/image/v1/json/image_client.py
+++ b/tempest/services/image/v1/json/image_client.py
@@ -121,28 +121,25 @@
         body = json.loads(''.join([c for c in body_iter]))
         return resp, body['image']
 
-    def create_image(self, name, container_format, disk_format, is_public=True,
-                     location=None, properties=None, data=None):
+    def create_image(self, name, container_format, disk_format, **kwargs):
         params = {
             "name": name,
             "container_format": container_format,
             "disk_format": disk_format,
-            "is_public": is_public,
         }
+
         headers = {}
 
-        if location is not None:
-            params['location'] = location
-
-        if properties is not None:
-            params['properties'] = properties
+        for option in ['is_public', 'location', 'properties']:
+            if option in kwargs:
+                params[option] = kwargs.get(option)
 
         headers.update(self._image_meta_to_headers(params))
 
-        if data is not None:
-            return self._create_with_data(headers, data)
+        if 'data' in kwargs:
+            return self._create_with_data(headers, kwargs.get('data'))
 
-        resp, body = self.post('v1/images', data, headers)
+        resp, body = self.post('v1/images', None, headers)
         body = json.loads(body)
         return resp, body['image']
 
diff --git a/tempest/services/image/v2/json/image_client.py b/tempest/services/image/v2/json/image_client.py
index bcae79b..2c50a8d 100644
--- a/tempest/services/image/v2/json/image_client.py
+++ b/tempest/services/image/v2/json/image_client.py
@@ -63,17 +63,20 @@
 
         jsonschema.validate(body, schema)
 
-    def create_image(self, name, container_format, disk_format, is_public=True,
-                     properties=None):
+    def create_image(self, name, container_format, disk_format, **kwargs):
         params = {
             "name": name,
             "container_format": container_format,
             "disk_format": disk_format,
         }
-        if is_public:
-            params["visibility"] = "public"
-        else:
-            params["visibility"] = "private"
+
+        for option in ['visibility']:
+            if option in kwargs:
+                value = kwargs.get(option)
+                if isinstance(value, dict) or isinstance(value, tuple):
+                    params.update(value)
+                else:
+                    params[option] = value
 
         data = json.dumps(params)
         self._validate_schema(data)
diff --git a/tempest/tests/image/base.py b/tempest/tests/image/base.py
new file mode 100644
index 0000000..6fb6e9f
--- /dev/null
+++ b/tempest/tests/image/base.py
@@ -0,0 +1,77 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 IBM Corp.
+#
+#    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 logging
+import time
+
+from tempest import clients
+from tempest.common.utils.data_utils import rand_name
+from tempest import exceptions
+import tempest.test
+
+LOG = logging.getLogger(__name__)
+
+
+class BaseImageTest(tempest.test.BaseTestCase):
+    """Base test class for Image API tests."""
+
+    @classmethod
+    def setUpClass(cls):
+        cls.os = clients.Manager()
+        cls.created_images = []
+
+    @classmethod
+    def tearDownClass(cls):
+        for image_id in cls.created_images:
+            try:
+                cls.client.delete_image(image_id)
+            except exceptions.NotFound:
+                pass
+
+        for image_id in cls.created_images:
+                cls.client.wait_for_resource_deletion(image_id)
+
+    @classmethod
+    def create_image(cls, **kwargs):
+        """Wrapper that returns a test image."""
+        name = rand_name(cls.__name__ + "-instance")
+
+        if 'name' in kwargs:
+            name = kwargs.pop('name')
+
+        container_format = kwargs.pop('container_format')
+        disk_format = kwargs.pop('disk_format')
+
+        resp, image = cls.client.create_image(name, container_format,
+                                              disk_format, **kwargs)
+        cls.created_images.append(image['id'])
+        return resp, image
+
+
+class BaseV1ImageTest(BaseImageTest):
+
+    @classmethod
+    def setUpClass(cls):
+        super(BaseV1ImageTest, cls).setUpClass()
+        cls.client = cls.os.image_client
+
+
+class BaseV2ImageTest(BaseImageTest):
+
+    @classmethod
+    def setUpClass(cls):
+        super(BaseV2ImageTest, cls).setUpClass()
+        cls.client = cls.os.image_client_v2
diff --git a/tempest/tests/image/v1/test_image_members.py b/tempest/tests/image/v1/test_image_members.py
index 30fa6c6..92052fc 100644
--- a/tempest/tests/image/v1/test_image_members.py
+++ b/tempest/tests/image/v1/test_image_members.py
@@ -17,42 +17,32 @@
 import cStringIO as StringIO
 
 from tempest import clients
-import tempest.test
+from tempest.tests.image import base
 
 
-class ImageMembersTests(tempest.test.BaseTestCase):
+class ImageMembersTests(base.BaseV1ImageTest):
 
     @classmethod
     def setUpClass(cls):
-        cls.os = clients.Manager()
-        cls.client = cls.os.image_client
+        super(ImageMembersTests, cls).setUpClass()
         admin = clients.AdminManager(interface='json')
         cls.admin_client = admin.identity_client
-        cls.created_images = []
         cls.tenants = cls._get_tenants()
 
     @classmethod
-    def tearDownClass(cls):
-        for image_id in cls.created_images:
-            cls.client.delete_image(image_id)
-            cls.client.wait_for_resource_deletion(image_id)
-
-    @classmethod
     def _get_tenants(cls):
         resp, tenants = cls.admin_client.list_tenants()
         tenants = map(lambda x: x['id'], tenants)
         return tenants
 
-    def _create_image(self, name=None):
+    def _create_image(self):
         image_file = StringIO.StringIO('*' * 1024)
-        if name is not None:
-            name = 'New Standard Image with Members'
-        resp, image = self.client.create_image(name,
-                                               'bare', 'raw',
-                                               is_public=True, data=image_file)
+        resp, image = self.create_image(container_format='bare',
+                                        disk_format='raw',
+                                        is_public=True,
+                                        data=image_file)
         self.assertEquals(201, resp.status)
         image_id = image['id']
-        self.created_images.append(image_id)
         return image_id
 
     def test_add_image_member(self):
@@ -69,8 +59,7 @@
         image = self._create_image()
         resp = self.client.add_member(self.tenants[0], image)
         self.assertEquals(204, resp.status)
-        name = 'Shared Image'
-        share_image = self._create_image(name=name)
+        share_image = self._create_image()
         resp = self.client.add_member(self.tenants[0], share_image)
         self.assertEquals(204, resp.status)
         resp, body = self.client.get_shared_images(self.tenants[0])
@@ -81,8 +70,7 @@
         self.assertIn(image, images)
 
     def test_remove_member(self):
-        name = 'Shared Image for Delete Test'
-        image_id = self._create_image(name=name)
+        image_id = self._create_image()
         resp = self.client.add_member(self.tenants[0], image_id)
         self.assertEquals(204, resp.status)
         resp = self.client.delete_member(self.tenants[0], image_id)
diff --git a/tempest/tests/image/v1/test_images.py b/tempest/tests/image/v1/test_images.py
index 84bb650..1888b28 100644
--- a/tempest/tests/image/v1/test_images.py
+++ b/tempest/tests/image/v1/test_images.py
@@ -19,26 +19,12 @@
 
 from tempest import clients
 from tempest import exceptions
-import tempest.test
 from tempest.test import attr
+from tempest.tests.image import base
 
 
-class CreateRegisterImagesTest(tempest.test.BaseTestCase):
-
-    """
-    Here we test the registration and creation of images
-    """
-
-    @classmethod
-    def setUpClass(cls):
-        cls.os = clients.Manager()
-        cls.client = cls.os.image_client
-        cls.created_images = []
-
-    @classmethod
-    def tearDownClass(cls):
-        for image_id in cls.created_images:
-            cls.client.delete(image_id)
+class CreateRegisterImagesTest(base.BaseV1ImageTest):
+    """Here we test the registration and creation of images."""
 
     @attr(type='negative')
     def test_register_with_invalid_container_format(self):
@@ -55,9 +41,11 @@
     def test_register_then_upload(self):
         # Register, then upload an image
         properties = {'prop1': 'val1'}
-        resp, body = self.client.create_image('New Name', 'bare', 'raw',
-                                              is_public=True,
-                                              properties=properties)
+        resp, body = self.create_image(name='New Name',
+                                       container_format='bare',
+                                       disk_format='raw',
+                                       is_public=True,
+                                       properties=properties)
         self.assertTrue('id' in body)
         image_id = body.get('id')
         self.created_images.append(image_id)
@@ -80,10 +68,11 @@
     @attr(type='image')
     def test_register_remote_image(self):
         # Register a new remote image
-        resp, body = self.client.create_image('New Remote Image', 'bare',
-                                              'raw', is_public=True,
-                                              location='http://example.com'
-                                                       '/someimage.iso')
+        resp, body = self.create_image(name='New Remote Image',
+                                       container_format='bare',
+                                       disk_format='raw', is_public=True,
+                                       location='http://example.com'
+                                                '/someimage.iso')
         self.assertTrue('id' in body)
         image_id = body.get('id')
         self.created_images.append(image_id)
@@ -95,7 +84,7 @@
         self.assertEqual('active', body.get('status'))
 
 
-class ListImagesTest(tempest.test.BaseTestCase):
+class ListImagesTest(base.BaseV1ImageTest):
 
     """
     Here we test the listing of image information
@@ -103,9 +92,7 @@
 
     @classmethod
     def setUpClass(cls):
-        cls.os = clients.Manager()
-        cls.client = cls.os.image_client
-        cls.created_images = []
+        super(ListImagesTest, cls).setUpClass()
 
         # We add a few images here to test the listing functionality of
         # the images API
@@ -132,12 +119,6 @@
         cls.dup_set = set((img3, img4))
 
     @classmethod
-    def tearDownClass(cls):
-        for image_id in cls.created_images:
-            cls.client.delete_image(image_id)
-            cls.client.wait_for_resource_deletion(image_id)
-
-    @classmethod
     def _create_remote_image(cls, name, container_format, disk_format):
         """
         Create a new remote image and return the ID of the newly-registered
@@ -145,12 +126,12 @@
         """
         name = 'New Remote Image %s' % name
         location = 'http://example.com/someimage_%s.iso' % name
-        resp, image = cls.client.create_image(name,
-                                              container_format, disk_format,
-                                              is_public=True,
-                                              location=location)
+        resp, image = cls.create_image(name=name,
+                                       container_format=container_format,
+                                       disk_format=disk_format,
+                                       is_public=True,
+                                       location=location)
         image_id = image['id']
-        cls.created_images.append(image_id)
         return image_id
 
     @classmethod
@@ -163,11 +144,11 @@
         """
         image_file = StringIO.StringIO('*' * size)
         name = 'New Standard Image %s' % name
-        resp, image = cls.client.create_image(name,
-                                              container_format, disk_format,
-                                              is_public=True, data=image_file)
+        resp, image = cls.create_image(name=name,
+                                       container_format=container_format,
+                                       disk_format=disk_format,
+                                       is_public=True, data=image_file)
         image_id = image['id']
-        cls.created_images.append(image_id)
         return image_id
 
     @attr(type='image')
diff --git a/tempest/tests/image/v2/test_images.py b/tempest/tests/image/v2/test_images.py
index 9abf28d..19a7a95 100644
--- a/tempest/tests/image/v2/test_images.py
+++ b/tempest/tests/image/v2/test_images.py
@@ -21,27 +21,16 @@
 
 from tempest import clients
 from tempest import exceptions
-import tempest.test
 from tempest.test import attr
+from tempest.tests.image import base
 
 
-class CreateRegisterImagesTest(tempest.test.BaseTestCase):
+class CreateRegisterImagesTest(base.BaseV2ImageTest):
 
     """
     Here we test the registration and creation of images
     """
 
-    @classmethod
-    def setUpClass(cls):
-        cls.os = clients.Manager()
-        cls.client = cls.os.image_client_v2
-        cls.created_images = []
-
-    @classmethod
-    def tearDownClass(cls):
-        for image_id in cls.created_images:
-            cls.client.delete(image_id)
-
     @attr(type='negative')
     def test_register_with_invalid_container_format(self):
         # Negative tests for invalid data supplied to POST /images
@@ -56,8 +45,10 @@
     @attr(type='image')
     def test_register_then_upload(self):
         # Register, then upload an image
-        resp, body = self.client.create_image('New Name', 'bare', 'raw',
-                                              is_public=True)
+        resp, body = self.create_image(name='New Name',
+                                       container_format='bare',
+                                       disk_format='raw',
+                                       visibility='public')
         self.assertTrue('id' in body)
         image_id = body.get('id')
         self.created_images.append(image_id)
@@ -77,7 +68,7 @@
         self.assertEqual(1024, body.get('size'))
 
 
-class ListImagesTest(tempest.test.BaseTestCase):
+class ListImagesTest(base.BaseV2ImageTest):
 
     """
     Here we test the listing of image information
@@ -85,22 +76,13 @@
 
     @classmethod
     def setUpClass(cls):
-        cls.os = clients.Manager()
-        cls.client = cls.os.image_client_v2
-        cls.created_images = []
-
+        super(ListImagesTest, cls).setUpClass()
         # We add a few images here to test the listing functionality of
         # the images API
         for x in xrange(0, 10):
             cls.created_images.append(cls._create_standard_image(x))
 
     @classmethod
-    def tearDownClass(cls):
-        for image_id in cls.created_images:
-            cls.client.delete_image(image_id)
-            cls.client.wait_for_resource_deletion(image_id)
-
-    @classmethod
     def _create_standard_image(cls, number):
         """
         Create a new standard image and return the ID of the newly-registered
@@ -109,8 +91,9 @@
         """
         image_file = StringIO.StringIO('*' * random.randint(1024, 4096))
         name = 'New Standard Image %s' % number
-        resp, body = cls.client.create_image(name, 'bare', 'raw',
-                                             is_public=True)
+        resp, body = cls.create_image(name=name, container_format='bare',
+                                      disk_format='raw',
+                                      visibility='public')
         image_id = body['id']
         resp, body = cls.client.store_image(image_id, data=image_file)