Merge "Add Tests for Groups Volume APIs - Part 3"
diff --git a/doc/source/microversion_testing.rst b/doc/source/microversion_testing.rst
index 336aa5b..d80081d 100644
--- a/doc/source/microversion_testing.rst
+++ b/doc/source/microversion_testing.rst
@@ -330,13 +330,9 @@
 
  .. _2.47: http://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id42
 
- * `2.52`_
+ * `2.48`_
 
- .. _2.52: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id47
-
- * `2.53`_
-
- .. _2.53: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id48
+ .. _2.48: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id43
 
 * Volume
 
diff --git a/releasenotes/notes/add-show-volume-image-metadata-api-to-v2-volumes-client-ee3c027f35276561.yaml b/releasenotes/notes/add-show-volume-image-metadata-api-to-v2-volumes-client-ee3c027f35276561.yaml
new file mode 100644
index 0000000..ac7c74e
--- /dev/null
+++ b/releasenotes/notes/add-show-volume-image-metadata-api-to-v2-volumes-client-ee3c027f35276561.yaml
@@ -0,0 +1,5 @@
+---
+features:
+  - |
+    Add show volume image metadata API to v2 volumes_client library.
+    This feature enables the possibility to show volume's image metadata.
diff --git a/releasenotes/notes/plugin-client-registration-enhancements-e09131742391225b.yaml b/releasenotes/notes/plugin-client-registration-enhancements-e09131742391225b.yaml
new file mode 100644
index 0000000..b6391b6
--- /dev/null
+++ b/releasenotes/notes/plugin-client-registration-enhancements-e09131742391225b.yaml
@@ -0,0 +1,12 @@
+---
+features:
+  - |
+    When registering service clients from installed plugins, all registrations
+    are now processed, even if one or more fails. All exceptions encountered
+    during the registration process are recorded.  If at least one exception
+    was encountered, the registration process fails and all interim errors are
+    reported.
+  - |
+    The __repr__ method is now implemented for the base `tempest.Exception`
+    class, its implementation is identical to __str__: it reports the error
+    message merged with input parameters.
diff --git a/tempest/api/compute/admin/test_aggregates.py b/tempest/api/compute/admin/test_aggregates.py
index 902ea9a..57d3983 100644
--- a/tempest/api/compute/admin/test_aggregates.py
+++ b/tempest/api/compute/admin/test_aggregates.py
@@ -125,7 +125,6 @@
             name=aggregate_name, availability_zone=az_name)
 
         self.assertEqual(az_name, aggregate['availability_zone'])
-        self.assertIsNotNone(aggregate['id'])
 
         aggregate_id = aggregate['id']
         new_aggregate_name = aggregate_name + '_new'
diff --git a/tempest/api/compute/admin/test_hosts.py b/tempest/api/compute/admin/test_hosts.py
index 0e1e7ed..00f3256 100644
--- a/tempest/api/compute/admin/test_hosts.py
+++ b/tempest/api/compute/admin/test_hosts.py
@@ -65,9 +65,4 @@
             resources = self.client.show_host(hostname)['host']
             self.assertNotEmpty(resources)
             host_resource = resources[0]['resource']
-            self.assertIsNotNone(host_resource)
-            self.assertIsNotNone(host_resource['cpu'])
-            self.assertIsNotNone(host_resource['disk_gb'])
-            self.assertIsNotNone(host_resource['memory_mb'])
-            self.assertIsNotNone(host_resource['project'])
             self.assertEqual(hostname, host_resource['host'])
diff --git a/tempest/api/compute/keypairs/test_keypairs.py b/tempest/api/compute/keypairs/test_keypairs.py
index a35e60a..3a54d51 100644
--- a/tempest/api/compute/keypairs/test_keypairs.py
+++ b/tempest/api/compute/keypairs/test_keypairs.py
@@ -51,13 +51,10 @@
         # Keypair should be created, verified and deleted
         k_name = data_utils.rand_name('keypair')
         keypair = self.create_keypair(k_name)
-        private_key = keypair['private_key']
         key_name = keypair['name']
         self.assertEqual(key_name, k_name,
                          "The created keypair name is not equal "
                          "to the requested name")
-        self.assertIsNotNone(private_key,
-                             "Field private_key is empty or not found.")
 
     @decorators.idempotent_id('a4233d5d-52d8-47cc-9a25-e1864527e3df')
     def test_get_keypair_detail(self):
@@ -68,9 +65,6 @@
         self.assertEqual(keypair_detail['name'], k_name,
                          "The created keypair name is not equal "
                          "to requested name")
-        public_key = keypair_detail['public_key']
-        self.assertIsNotNone(public_key,
-                             "Field public_key is empty or not found.")
 
     @decorators.idempotent_id('39c90c6a-304a-49dd-95ec-2366129def05')
     def test_keypair_create_with_pub_key(self):
diff --git a/tempest/api/compute/servers/test_virtual_interfaces.py b/tempest/api/compute/servers/test_virtual_interfaces.py
index 6b625d9..a42b968 100644
--- a/tempest/api/compute/servers/test_virtual_interfaces.py
+++ b/tempest/api/compute/servers/test_virtual_interfaces.py
@@ -56,7 +56,6 @@
                 self.client.list_virtual_interfaces(self.server['id'])
         else:
             output = self.client.list_virtual_interfaces(self.server['id'])
-            self.assertIsNotNone(output)
             virt_ifaces = output
             self.assertNotEmpty(virt_ifaces['virtual_interfaces'],
                                 'Expected virtual interfaces, got 0 '
diff --git a/tempest/api/compute/volumes/test_volumes_get.py b/tempest/api/compute/volumes/test_volumes_get.py
index cb6ab43..d83d49e 100644
--- a/tempest/api/compute/volumes/test_volumes_get.py
+++ b/tempest/api/compute/volumes/test_volumes_get.py
@@ -55,8 +55,6 @@
         self.assertEqual(volume['displayName'], v_name,
                          "The created volume name is not equal "
                          "to the requested name")
-        self.assertIsNotNone(volume['id'],
-                             "Field volume id is empty or not found.")
         # GET Volume
         fetched_volume = self.volumes_client.show_volume(
             volume['id'])['volume']
diff --git a/tempest/api/volume/test_image_metadata.py b/tempest/api/volume/test_image_metadata.py
index 77baf18..129981b 100644
--- a/tempest/api/volume/test_image_metadata.py
+++ b/tempest/api/volume/test_image_metadata.py
@@ -40,7 +40,7 @@
 
     @decorators.idempotent_id('03efff0b-5c75-4822-8f10-8789ac15b13e')
     @test.services('image')
-    def test_update_image_metadata(self):
+    def test_update_show_delete_image_metadata(self):
         # Update image metadata
         image_metadata = {'image_id': '5137a025-3c5f-43c1-bc64-5f41270040a5',
                           'image_name': 'image',
@@ -49,7 +49,7 @@
         self.volumes_client.update_volume_image_metadata(self.volume['id'],
                                                          **image_metadata)
 
-        # Fetch image metadata from the volume
+        # Fetch volume's image metadata by show_volume method
         volume_image_metadata = self.volumes_client.show_volume(
             self.volume['id'])['volume']['volume_image_metadata']
 
@@ -62,9 +62,9 @@
                                                          'ramdisk_id')
         del image_metadata['ramdisk_id']
 
-        # Fetch the new image metadata from the volume
-        volume_image_metadata = self.volumes_client.show_volume(
-            self.volume['id'])['volume']['volume_image_metadata']
+        # Fetch volume's image metadata by show_volume_image_metadata method
+        volume_image_metadata = self.volumes_client.show_volume_image_metadata(
+            self.volume['id'])['metadata']
 
         # Verify image metadata was updated after item deletion
         self.assertThat(volume_image_metadata.items(),
diff --git a/tempest/common/credentials_factory.py b/tempest/common/credentials_factory.py
index 5dc1a3e..cecb8e3 100644
--- a/tempest/common/credentials_factory.py
+++ b/tempest/common/credentials_factory.py
@@ -265,8 +265,6 @@
     return credentials
 
 
-# Wrapper around auth.get_credentials to use the configured identity version
-# if none is specified
 def get_credentials(fill_in=True, identity_version=None, **kwargs):
     """Get credentials from dict based on config
 
diff --git a/tempest/lib/services/volume/v2/volumes_client.py b/tempest/lib/services/volume/v2/volumes_client.py
index 62b9992..e932adc 100644
--- a/tempest/lib/services/volume/v2/volumes_client.py
+++ b/tempest/lib/services/volume/v2/volumes_client.py
@@ -355,6 +355,15 @@
         self.expected_success(200, resp.status)
         return rest_client.ResponseBody(resp, body)
 
+    def show_volume_image_metadata(self, volume_id):
+        """Show image metadata for the volume."""
+        post_body = json.dumps({'os-show_image_metadata': {}})
+        url = "volumes/%s/action" % volume_id
+        resp, body = self.post(url, post_body)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
     @removals.remove(message="use list_pools from tempest.lib.services."
                              "volume.v2.scheduler_stats_client")
     def show_pools(self, detail=False):
diff --git a/tempest/tests/lib/services/volume/v2/test_volumes_client.py b/tempest/tests/lib/services/volume/v2/test_volumes_client.py
index e53e0a2..d7b042e 100644
--- a/tempest/tests/lib/services/volume/v2/test_volumes_client.py
+++ b/tempest/tests/lib/services/volume/v2/test_volumes_client.py
@@ -13,6 +13,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from oslo_serialization import jsonutils as json
+
 from tempest.lib.services.volume.v2 import volumes_client
 from tempest.tests.lib import fake_auth_provider
 from tempest.tests.lib.services import base
@@ -26,6 +28,19 @@
         }
     }
 
+    FAKE_VOLUME_IMAGE_METADATA = {
+        "metadata": {
+            "container_format": "bare",
+            "min_ram": "0",
+            "disk_format": "raw",
+            "image_name": "xly-ubuntu16-server",
+            "image_id": "3e087b0c-10c5-4255-b147-6e8e9dbad6fc",
+            "checksum": "008f5d22fe3cb825d714da79607a90f9",
+            "min_disk": "0",
+            "size": "8589934592"
+        }
+    }
+
     def setUp(self):
         super(TestVolumesClient, self).setUp()
         fake_auth = fake_auth_provider.FakeAuthProvider()
@@ -76,6 +91,17 @@
             volume_id="a3be971b-8de5-4bdf-bdb8-3d8eb0fb69f8",
             id="key1")
 
+    def _test_show_volume_image_metadata(self, bytes_body=False):
+        fake_volume_id = "a3be971b-8de5-4bdf-bdb8-3d8eb0fb69f8"
+        self.check_service_client_function(
+            self.client.show_volume_image_metadata,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_VOLUME_IMAGE_METADATA,
+            to_utf=bytes_body,
+            mock_args=['volumes/%s/action' % fake_volume_id,
+                       json.dumps({"os-show_image_metadata": {}})],
+            volume_id=fake_volume_id)
+
     def test_force_detach_volume_with_str_body(self):
         self._test_force_detach_volume()
 
@@ -88,6 +114,12 @@
     def test_show_volume_metadata_item_with_bytes_body(self):
         self._test_show_volume_metadata_item(bytes_body=True)
 
+    def test_show_volume_image_metadata_with_str_body(self):
+        self._test_show_volume_image_metadata()
+
+    def test_show_volume_image_metadata_with_bytes_body(self):
+        self._test_show_volume_image_metadata(bytes_body=True)
+
     def test_retype_volume_with_str_body(self):
         self._test_retype_volume()