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.