Make scenario snapshot tests work with ephemeral|swap

Currently, the create_server_snapshot and
test_image_defined_boot_from_volume methods assume that the server
being snapshotted will have only one block device mapping. However, if
the server has an ephemeral disk and/or a swap disk, it will have
multiple block device mappings.

This can cause erroneous scenario test failures if the first block
device mapping in the image service response is not the root disk.

This adjusts create_server_snapshot and
test_image_defined_boot_from_volume to check for a non-null snapshot_id
before using it.

Change-Id: I2417d5a91ae5fbb2101b64a07b6b8c74199e8fb5
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 7c986cc..a809342 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -898,6 +898,19 @@
         if not isinstance(exc, lib_exc.SSHTimeout):
             LOG.debug('Network information on a devstack host')
 
+    def get_snapshot_id(self, bdms):
+        if isinstance(bdms, str):
+            bdms = json.loads(bdms)
+        snapshot_id = None
+        for bdm in bdms:
+            # Look for the block device mapping that actually has a
+            # snapshot. If the server has ephemeral or swap disk, their
+            # block device mappings will be present with snapshot_id = None
+            if 'snapshot_id' in bdm and bdm['snapshot_id'] is not None:
+                snapshot_id = bdm['snapshot_id']
+                break
+        return snapshot_id
+
     def create_server_snapshot(self, server, name=None, **kwargs):
         """Creates server snapshot"""
         # Glance client
@@ -924,20 +937,19 @@
         snapshot_image = _image_client.show_image(image_id)
         image_props = snapshot_image
 
-        bdm = image_props.get('block_device_mapping')
-        if bdm:
-            bdm = json.loads(bdm)
-            if bdm and 'snapshot_id' in bdm[0]:
-                snapshot_id = bdm[0]['snapshot_id']
-                self.addCleanup(
-                    self.snapshots_client.wait_for_resource_deletion,
-                    snapshot_id)
-                self.addCleanup(test_utils.call_and_ignore_notfound_exc,
-                                self.snapshots_client.delete_snapshot,
-                                snapshot_id)
-                waiters.wait_for_volume_resource_status(self.snapshots_client,
-                                                        snapshot_id,
-                                                        'available')
+        bdms = image_props.get('block_device_mapping')
+        if bdms:
+            snapshot_id = self.get_snapshot_id(bdms)
+            self.assertIsNotNone(snapshot_id)
+            self.addCleanup(
+                self.snapshots_client.wait_for_resource_deletion,
+                snapshot_id)
+            self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                            self.snapshots_client.delete_snapshot,
+                            snapshot_id)
+            waiters.wait_for_volume_resource_status(
+                self.snapshots_client, snapshot_id, 'available')
+
         image_name = snapshot_image['name']
         self.assertEqual(name, image_name)
         LOG.debug("Created snapshot image %s for server %s",
diff --git a/tempest/scenario/test_volume_boot_pattern.py b/tempest/scenario/test_volume_boot_pattern.py
index 5e28ecd..febc2f6 100644
--- a/tempest/scenario/test_volume_boot_pattern.py
+++ b/tempest/scenario/test_volume_boot_pattern.py
@@ -11,7 +11,6 @@
 #    under the License.
 
 from oslo_log import log as logging
-from oslo_serialization import jsonutils as json
 import testtools
 
 from tempest.common import utils
@@ -245,8 +244,7 @@
         bdms = image.get('block_device_mapping')
         if not bdms:
             bdms = image['properties']['block_device_mapping']
-        bdms = json.loads(bdms)
-        snapshot_id = bdms[0]['snapshot_id']
+        snapshot_id = self.get_snapshot_id(bdms)
         self._delete_snapshot(snapshot_id)
 
         # Now, delete the first server which will also delete the first