Add image dependencies test for image <-> instance snapshot

Detect problems related to Glance images not allowing
Glance images to be deleted.

Adds new option:
  CONF.volume_feature_enabled.enable_volume_image_dep_tests

Change-Id: Ia216f5be377bf5c35d7addb78d7f8aabb2393405
diff --git a/releasenotes/notes/add-enable-volume-image-dep-tests-option-150b929d18da233f.yaml b/releasenotes/notes/add-enable-volume-image-dep-tests-option-150b929d18da233f.yaml
new file mode 100644
index 0000000..979f083
--- /dev/null
+++ b/releasenotes/notes/add-enable-volume-image-dep-tests-option-150b929d18da233f.yaml
@@ -0,0 +1,6 @@
+---
+features:
+  - |
+    Add new config option 'enable-volume-image-dep-tests' in section
+    [volume-feature-enabled] which should be used in
+    image<->volume<->snapshot dependency tests.
diff --git a/tempest/api/image/v2/test_images_dependency.py b/tempest/api/image/v2/test_images_dependency.py
new file mode 100644
index 0000000..326045b
--- /dev/null
+++ b/tempest/api/image/v2/test_images_dependency.py
@@ -0,0 +1,103 @@
+# Copyright 2024 OpenStack Foundation
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import io
+
+from oslo_log import log as logging
+
+from tempest.api.compute import base as compute_base
+from tempest.api.image import base as image_base
+from tempest.common import utils
+from tempest.common import waiters
+from tempest import config
+from tempest.lib.common.utils import data_utils
+from tempest.lib import decorators
+from tempest.scenario import manager
+
+CONF = config.CONF
+LOG = logging.getLogger(__name__)
+
+
+class ImageDependencyTests(image_base.BaseV2ImageTest,
+                           compute_base.BaseV2ComputeTest,
+                           manager.ScenarioTest):
+    """Test image, instance, and snapshot dependency.
+
+       The tests create image and remove the base image that other snapshots
+       were depend on.In OpenStack, images and snapshots should be separate,
+       but in some configurations like Glance with Ceph storage,
+       there were cases where images couldn't be removed.
+       This was fixed in glance store for RBD backend.
+
+       * Dependency scenarios:
+           - image > instance -> snapshot dependency
+
+       NOTE: volume -> image dependencies tests are in cinder-tempest-plugin
+    """
+
+    @classmethod
+    def skip_checks(cls):
+        super(ImageDependencyTests, cls).skip_checks()
+        if not CONF.volume_feature_enabled.enable_volume_image_dep_tests:
+            skip_msg = (
+                "%s Volume/image dependency tests "
+                "not enabled" % (cls.__name__))
+            raise cls.skipException(skip_msg)
+
+    def _create_instance_snapshot(self):
+        """Create instance from image and then snapshot the instance."""
+        # Create image and store data to image
+        image_name = data_utils.rand_name(
+            prefix=CONF.resource_name_prefix,
+            name='image-dependency-test')
+        image = self.create_image(name=image_name,
+                                  container_format='bare',
+                                  disk_format='raw',
+                                  visibility='private')
+        file_content = data_utils.random_bytes()
+        image_file = io.BytesIO(file_content)
+        self.client.store_image_file(image['id'], image_file)
+        waiters.wait_for_image_status(
+            self.client, image['id'], 'active')
+        # Create instance
+        instance = self.create_test_server(
+            name='instance-depend-image',
+            image_id=image['id'],
+            wait_until='ACTIVE')
+        LOG.info("Instance from image is created %s", instance)
+        instance_observed = \
+            self.servers_client.show_server(instance['id'])['server']
+        # Create instance snapshot
+        snapshot_instance = self.create_server_snapshot(
+            server=instance_observed)
+        LOG.info("Instance snapshot is created %s", snapshot_instance)
+        return image['id'], snapshot_instance['id']
+
+    @decorators.idempotent_id('d19b0731-e98e-4103-8b0e-02f651b8f586')
+    @utils.services('compute')
+    def test_nova_image_snapshot_dependency(self):
+        """Test with image > instance > snapshot dependency.
+
+        Create instance snapshot and check if we able to delete base
+        image
+
+        """
+        base_image_id, snapshot_image_id = self._create_instance_snapshot()
+        self.client.delete_image(base_image_id)
+        self.client.wait_for_resource_deletion(base_image_id)
+        images_list = self.client.list_images()['images']
+        fetched_images_id = [img['id'] for img in images_list]
+        self.assertNotIn(base_image_id, fetched_images_id)
+        self.assertIn(snapshot_image_id, fetched_images_id)
diff --git a/tempest/config.py b/tempest/config.py
index fc50db5..abab70f 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -1071,7 +1071,10 @@
                default=None,
                help='Volume types used for data volumes. Multiple volume '
                     'types can be assigned.'),
-
+    cfg.BoolOpt('enable-volume-image-dep-tests',
+                default=True,
+                help='Run tests for dependencies between images, volumes'
+                'and instance snapshots')
 ]
 
 
@@ -1176,7 +1179,7 @@
                default='icmp',
                choices=('icmp', 'tcp', 'udp'),
                help='The protocol used in security groups tests to check '
-                    'connectivity.'),
+                    'connectivity.')
 ]