Check which glance client version to use

Nova is working toward supporting the glance v2 API which will
allow disabling the glance v1 API in glance in a deployment and
configuring Nova to only use the glance v2 API.

The existing API and scenario tests are setting up a base test image
using the glance v1 client regardless of whether or not the glance v1
API is enabled in tempest.conf.

This adds a check to the tests to see which glance client version
to use depending on the config.

Change-Id: I1b1dc2911698d4e4d1a2727902ba457688d88752
Closes-Bug: #1589093
diff --git a/tempest/api/compute/images/test_image_metadata.py b/tempest/api/compute/images/test_image_metadata.py
index 0724566..427a748 100644
--- a/tempest/api/compute/images/test_image_metadata.py
+++ b/tempest/api/compute/images/test_image_metadata.py
@@ -19,6 +19,7 @@
 from tempest.common.utils import data_utils
 from tempest.common import waiters
 from tempest import config
+from tempest import exceptions
 from tempest import test
 
 CONF = config.CONF
@@ -36,7 +37,17 @@
     @classmethod
     def setup_clients(cls):
         super(ImagesMetadataTestJSON, cls).setup_clients()
-        cls.glance_client = cls.os.image_client
+        # Check if glance v1 is available to determine which client to use. We
+        # prefer glance v1 for the compute API tests since the compute image
+        # API proxy was written for glance v1.
+        if CONF.image_feature_enabled.api_v1:
+            cls.glance_client = cls.os.image_client
+        elif CONF.image_feature_enabled.api_v2:
+            cls.glance_client = cls.os.image_client_v2
+        else:
+            raise exceptions.InvalidConfiguration(
+                'Either api_v1 or api_v2 must be True in '
+                '[image-feature-enabled].')
         cls.client = cls.compute_images_client
 
     @classmethod
@@ -45,14 +56,22 @@
         cls.image_id = None
 
         name = data_utils.rand_name('image')
+        if CONF.image_feature_enabled.api_v1:
+            kwargs = dict(is_public=False)
+        else:
+            kwargs = dict(visibility='private')
         body = cls.glance_client.create_image(name=name,
                                               container_format='bare',
                                               disk_format='raw',
-                                              is_public=False)['image']
+                                              **kwargs)
+        body = body['image'] if 'image' in body else body
         cls.image_id = body['id']
         cls.images.append(cls.image_id)
         image_file = six.StringIO(('*' * 1024))
-        cls.glance_client.update_image(cls.image_id, data=image_file)
+        if CONF.image_feature_enabled.api_v1:
+            cls.glance_client.update_image(cls.image_id, data=image_file)
+        else:
+            cls.glance_client.store_image_file(cls.image_id, data=image_file)
         waiters.wait_for_image_status(cls.client, cls.image_id, 'ACTIVE')
 
     def setUp(self):
diff --git a/tempest/api/compute/images/test_list_image_filters.py b/tempest/api/compute/images/test_list_image_filters.py
index af840cc..e74ca67 100644
--- a/tempest/api/compute/images/test_list_image_filters.py
+++ b/tempest/api/compute/images/test_list_image_filters.py
@@ -22,6 +22,7 @@
 from tempest.common.utils import data_utils
 from tempest.common import waiters
 from tempest import config
+from tempest import exceptions
 from tempest import test
 
 CONF = config.CONF
@@ -40,7 +41,17 @@
     def setup_clients(cls):
         super(ListImageFiltersTestJSON, cls).setup_clients()
         cls.client = cls.compute_images_client
-        cls.glance_client = cls.os.image_client
+        # Check if glance v1 is available to determine which client to use. We
+        # prefer glance v1 for the compute API tests since the compute image
+        # API proxy was written for glance v1.
+        if CONF.image_feature_enabled.api_v1:
+            cls.glance_client = cls.os.image_client
+        elif CONF.image_feature_enabled.api_v2:
+            cls.glance_client = cls.os.image_client_v2
+        else:
+            raise exceptions.InvalidConfiguration(
+                'Either api_v1 or api_v2 must be True in '
+                '[image-feature-enabled].')
 
     @classmethod
     def resource_setup(cls):
@@ -48,17 +59,25 @@
 
         def _create_image():
             name = data_utils.rand_name('image')
+            if CONF.image_feature_enabled.api_v1:
+                kwargs = dict(is_public=False)
+            else:
+                kwargs = dict(visibility='private')
             body = cls.glance_client.create_image(name=name,
                                                   container_format='bare',
                                                   disk_format='raw',
-                                                  is_public=False)['image']
+                                                  **kwargs)
+            body = body['image'] if 'image' in body else body
             image_id = body['id']
             cls.images.append(image_id)
             # Wait 1 second between creation and upload to ensure a delta
             # between created_at and updated_at.
             time.sleep(1)
             image_file = six.StringIO(('*' * 1024))
-            cls.glance_client.update_image(image_id, data=image_file)
+            if CONF.image_feature_enabled.api_v1:
+                cls.glance_client.update_image(image_id, data=image_file)
+            else:
+                cls.glance_client.store_image_file(image_id, data=image_file)
             waiters.wait_for_image_status(cls.client, image_id, 'ACTIVE')
             body = cls.client.show_image(image_id)['image']
             return body
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index 66aec84..3a4428a 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -24,6 +24,7 @@
 from tempest.common.utils.linux import remote_client
 from tempest.common import waiters
 from tempest import config
+from tempest import exceptions
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
 from tempest import test
@@ -320,6 +321,19 @@
     def test_create_backup(self):
         # Positive test:create backup successfully and rotate backups correctly
         # create the first and the second backup
+
+        # Check if glance v1 is available to determine which client to use. We
+        # prefer glance v1 for the compute API tests since the compute image
+        # API proxy was written for glance v1.
+        if CONF.image_feature_enabled.api_v1:
+            glance_client = self.os.image_client
+        elif CONF.image_feature_enabled.api_v2:
+            glance_client = self.os.image_client_v2
+        else:
+            raise exceptions.InvalidConfiguration(
+                'Either api_v1 or api_v2 must be True in '
+                '[image-feature-enabled].')
+
         backup1 = data_utils.rand_name('backup-1')
         resp = self.client.create_backup(self.server_id,
                                          backup_type='daily',
@@ -331,7 +345,7 @@
         def _clean_oldest_backup(oldest_backup):
             if oldest_backup_exist:
                 try:
-                    self.os.image_client.delete_image(oldest_backup)
+                    glance_client.delete_image(oldest_backup)
                 except lib_exc.NotFound:
                     pass
                 else:
@@ -341,7 +355,7 @@
 
         image1_id = data_utils.parse_image_id(resp['location'])
         self.addCleanup(_clean_oldest_backup, image1_id)
-        waiters.wait_for_image_status(self.os.image_client,
+        waiters.wait_for_image_status(glance_client,
                                       image1_id, 'active')
 
         backup2 = data_utils.rand_name('backup-2')
@@ -351,8 +365,8 @@
                                          rotation=2,
                                          name=backup2).response
         image2_id = data_utils.parse_image_id(resp['location'])
-        self.addCleanup(self.os.image_client.delete_image, image2_id)
-        waiters.wait_for_image_status(self.os.image_client,
+        self.addCleanup(glance_client.delete_image, image2_id)
+        waiters.wait_for_image_status(glance_client,
                                       image2_id, 'active')
 
         # verify they have been created
@@ -361,12 +375,20 @@
             'backup_type': "daily",
             'instance_uuid': self.server_id,
         }
-        image_list = self.os.image_client.list_images(
-            detail=True,
-            properties=properties,
-            status='active',
-            sort_key='created_at',
-            sort_dir='asc')['images']
+        if CONF.image_feature_enabled.api_v1:
+            params = dict(
+                properties=properties, status='active',
+                sort_key='created_at', sort_dir='asc',)
+            image_list = glance_client.list_images(
+                detail=True,
+                **params)['images']
+        else:
+            # Additional properties are flattened in glance v2.
+            params = dict(
+                status='active', sort_key='created_at', sort_dir='asc')
+            params.update(properties)
+            image_list = glance_client.list_images(params)['images']
+
         self.assertEqual(2, len(image_list))
         self.assertEqual((backup1, backup2),
                          (image_list[0]['name'], image_list[1]['name']))
@@ -380,17 +402,16 @@
                                          rotation=2,
                                          name=backup3).response
         image3_id = data_utils.parse_image_id(resp['location'])
-        self.addCleanup(self.os.image_client.delete_image, image3_id)
+        self.addCleanup(glance_client.delete_image, image3_id)
         # the first back up should be deleted
         waiters.wait_for_server_status(self.client, self.server_id, 'ACTIVE')
-        self.os.image_client.wait_for_resource_deletion(image1_id)
+        glance_client.wait_for_resource_deletion(image1_id)
         oldest_backup_exist = False
-        image_list = self.os.image_client.list_images(
-            detail=True,
-            properties=properties,
-            status='active',
-            sort_key='created_at',
-            sort_dir='asc')['images']
+        if CONF.image_feature_enabled.api_v1:
+            image_list = glance_client.list_images(
+                detail=True, **params)['images']
+        else:
+            image_list = glance_client.list_images(params)['images']
         self.assertEqual(2, len(image_list),
                          'Unexpected number of images for '
                          'v2:test_create_backup; was the oldest backup not '
diff --git a/tempest/api/volume/test_volumes_actions.py b/tempest/api/volume/test_volumes_actions.py
index 38913ce..76cd36c 100644
--- a/tempest/api/volume/test_volumes_actions.py
+++ b/tempest/api/volume/test_volumes_actions.py
@@ -18,6 +18,7 @@
 from tempest.common.utils import data_utils
 from tempest.common import waiters
 from tempest import config
+from tempest import exceptions
 from tempest.lib.common.utils import test_utils
 from tempest import test
 
@@ -30,7 +31,16 @@
     def setup_clients(cls):
         super(VolumesV2ActionsTest, cls).setup_clients()
         cls.client = cls.volumes_client
-        cls.image_client = cls.os.image_client
+        if CONF.service_available.glance:
+            # Check if glance v1 is available to determine which client to use.
+            if CONF.image_feature_enabled.api_v1:
+                cls.image_client = cls.os.image_client
+            elif CONF.image_feature_enabled.api_v2:
+                cls.image_client = cls.os.image_client_v2
+            else:
+                raise exceptions.InvalidConfiguration(
+                    'Either api_v1 or api_v2 must be True in '
+                    '[image-feature-enabled].')
 
     @classmethod
     def resource_setup(cls):
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 8d0f2f6..bab875d 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -50,8 +50,15 @@
         cls.compute_floating_ips_client = (
             cls.manager.compute_floating_ips_client)
         if CONF.service_available.glance:
-            # Glance image client v1
-            cls.image_client = cls.manager.image_client
+            # Check if glance v1 is available to determine which client to use.
+            if CONF.image_feature_enabled.api_v1:
+                cls.image_client = cls.manager.image_client
+            elif CONF.image_feature_enabled.api_v2:
+                cls.image_client = cls.manager.image_client_v2
+            else:
+                raise exceptions.InvalidConfiguration(
+                    'Either api_v1 or api_v2 must be True in '
+                    '[image-feature-enabled].')
         # Compute image client
         cls.compute_images_client = cls.manager.compute_images_client
         cls.keypairs_client = cls.manager.keypairs_client
@@ -376,14 +383,23 @@
             'name': name,
             'container_format': fmt,
             'disk_format': disk_format or fmt,
-            'is_public': 'False',
         }
-        params['properties'] = properties
-        image = self.image_client.create_image(**params)['image']
+        if CONF.image_feature_enabled.api_v1:
+            params['is_public'] = 'False'
+            params['properties'] = properties
+        else:
+            params['visibility'] = 'private'
+            # Additional properties are flattened out in the v2 API.
+            params.update(properties)
+        body = self.image_client.create_image(**params)
+        image = body['image'] if 'image' in body else body
         self.addCleanup(self.image_client.delete_image, image['id'])
         self.assertEqual("queued", image['status'])
         with open(path, 'rb') as image_file:
-            self.image_client.update_image(image['id'], data=image_file)
+            if CONF.image_feature_enabled.api_v1:
+                self.image_client.update_image(image['id'], data=image_file)
+            else:
+                self.image_client.store_image_file(image['id'], image_file)
         return image['id']
 
     def glance_image_create(self):
@@ -450,9 +466,16 @@
             thing_id=image_id, thing_id_param='id',
             cleanup_callable=test_utils.call_and_ignore_notfound_exc,
             cleanup_args=[_image_client.delete_image, image_id])
-        snapshot_image = _image_client.check_image(image_id)
+        if CONF.image_feature_enabled.api_v1:
+            # In glance v1 the additional properties are stored in the headers.
+            snapshot_image = _image_client.check_image(image_id)
+            image_props = snapshot_image.get('properties', {})
+        else:
+            # In glance v2 the additional properties are flattened.
+            snapshot_image = _image_client.show_image(image_id)
+            image_props = snapshot_image
 
-        bdm = snapshot_image.get('properties', {}).get('block_device_mapping')
+        bdm = image_props.get('block_device_mapping')
         if bdm:
             bdm = json.loads(bdm)
             if bdm and 'snapshot_id' in bdm[0]: