Add glance multistore tests

Adding tests for below scenarios

 1. test for import_to_all_stores(--all-stores true)

 2. test for import_to_specified_stores(--stores store_list)

Change-Id: I3aa413c624881065a1cf46c6345f9eda923df8eb
diff --git a/tempest/api/image/base.py b/tempest/api/image/base.py
index ae7b3e4..d3dc19a 100644
--- a/tempest/api/image/base.py
+++ b/tempest/api/image/base.py
@@ -18,6 +18,7 @@
 from tempest import config
 from tempest.lib.common.utils import data_utils
 from tempest.lib.common.utils import test_utils
+from tempest.lib import exceptions
 import tempest.test
 
 CONF = config.CONF
@@ -155,6 +156,15 @@
                         namespace_name)
         return namespace
 
+    @classmethod
+    def get_available_stores(cls):
+        stores = []
+        try:
+            stores = cls.client.info_stores()['stores']
+        except exceptions.NotFound:
+            pass
+        return stores
+
 
 class BaseV2MemberImageTest(BaseV2ImageTest):
 
diff --git a/tempest/api/image/v2/test_images.py b/tempest/api/image/v2/test_images.py
index c1a7211..28299a4 100644
--- a/tempest/api/image/v2/test_images.py
+++ b/tempest/api/image/v2/test_images.py
@@ -20,6 +20,7 @@
 
 from oslo_log import log as logging
 from tempest.api.image import base
+from tempest.common import waiters
 from tempest import config
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
@@ -113,6 +114,95 @@
         self.client.wait_for_resource_activation(image['id'])
 
 
+class MultiStoresImportImagesTest(base.BaseV2ImageTest):
+    """Test importing image in multiple stores"""
+    @classmethod
+    def skip_checks(cls):
+        super(MultiStoresImportImagesTest, cls).skip_checks()
+        if not CONF.image_feature_enabled.import_image:
+            skip_msg = (
+                "%s skipped as image import is not available" % cls.__name__)
+            raise cls.skipException(skip_msg)
+
+    @classmethod
+    def resource_setup(cls):
+        super(MultiStoresImportImagesTest, cls).resource_setup()
+        cls.available_import_methods = cls.client.info_import()[
+            'import-methods']['value']
+        if not cls.available_import_methods:
+            raise cls.skipException('Server does not support '
+                                    'any import method')
+
+        # NOTE(pdeore): Skip if glance-direct import method and mutlistore
+        # are not enabled/configured, or only one store is configured in
+        # multiple stores setup.
+        cls.available_stores = cls.get_available_stores()
+        if ('glance-direct' not in cls.available_import_methods or
+                not len(cls.available_stores) > 1):
+            raise cls.skipException(
+                'Either glance-direct import method not present in %s or '
+                'None or only one store is '
+                'configured %s' % (cls.available_import_methods,
+                                   cls.available_stores))
+
+    def _create_and_stage_image(self, all_stores=False):
+        """Create Image & stage image file for glance-direct import method."""
+        image_name = data_utils.rand_name('test-image')
+        container_format = CONF.image.container_formats[0]
+        disk_format = CONF.image.disk_formats[0]
+        image = self.create_image(name=image_name,
+                                  container_format=container_format,
+                                  disk_format=disk_format,
+                                  visibility='private')
+        self.assertEqual('queued', image['status'])
+
+        self.client.stage_image_file(
+            image['id'],
+            six.BytesIO(data_utils.random_bytes(10485760)))
+        # Check image status is 'uploading'
+        body = self.client.show_image(image['id'])
+        self.assertEqual(image['id'], body['id'])
+        self.assertEqual('uploading', body['status'])
+
+        if all_stores:
+            stores_list = ','.join([store['id']
+                                    for store in self.available_stores])
+        else:
+            stores = [store['id'] for store in self.available_stores]
+            stores_list = stores[::len(stores) - 1]
+
+        return body, stores_list
+
+    @decorators.idempotent_id('bf04ff00-3182-47cb-833a-f1c6767b47fd')
+    def test_glance_direct_import_image_to_all_stores(self):
+        """Test image is imported in all available stores
+
+        Create image, import image to all available stores using glance-direct
+        import method and verify that import succeeded.
+        """
+        image, stores = self._create_and_stage_image(all_stores=True)
+
+        self.client.image_import(
+            image['id'], method='glance-direct', all_stores=True)
+
+        waiters.wait_for_image_imported_to_stores(self.client,
+                                                  image['id'], stores)
+
+    @decorators.idempotent_id('82fb131a-dd2b-11ea-aec7-340286b6c574')
+    def test_glance_direct_import_image_to_specific_stores(self):
+        """Test image is imported in all available stores
+
+        Create image, import image to specified store(s) using glance-direct
+        import method and verify that import succeeded.
+        """
+        image, stores = self._create_and_stage_image()
+        self.client.image_import(image['id'], method='glance-direct',
+                                 stores=stores)
+
+        waiters.wait_for_image_imported_to_stores(self.client, image['id'],
+                                                  (','.join(stores)))
+
+
 class BasicOperationsImagesTest(base.BaseV2ImageTest):
     """Here we test the basic operations of images"""
 
diff --git a/tempest/common/waiters.py b/tempest/common/waiters.py
index fc25914..cc8778b 100644
--- a/tempest/common/waiters.py
+++ b/tempest/common/waiters.py
@@ -187,6 +187,28 @@
     raise lib_exc.TimeoutException(message)
 
 
+def wait_for_image_imported_to_stores(client, image_id, stores):
+    """Waits for an image to be imported to all requested stores.
+
+    The client should also have build_interval and build_timeout attributes.
+    """
+
+    start = int(time.time())
+    while int(time.time()) - start < client.build_timeout:
+        image = client.show_image(image_id)
+        if image['status'] == 'active' and image['stores'] == stores:
+            return
+
+        time.sleep(client.build_interval)
+
+    message = ('Image %(image_id)s failed to import '
+               'on stores: %s' % str(image['os_glance_failed_import']))
+    caller = test_utils.find_test_caller()
+    if caller:
+        message = '(%s) %s' % (caller, message)
+    raise lib_exc.TimeoutException(message)
+
+
 def wait_for_volume_resource_status(client, resource_id, status):
     """Waits for a volume resource to reach a given status.