Merge "Remove sync call in md5 data integrity tests"
diff --git a/.zuul.yaml b/.zuul.yaml
index 2b6b59c..e0a0b2e 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -21,13 +21,11 @@
- cinder-tempest-plugin-cbak-ceph
- cinder-tempest-plugin-cbak-s3
# As per the Tempest "Stable Branch Support Policy", Tempest will only
- # support the "Maintained" stable branches and not the "Extended Maintained"
- # branches. That is what we need to do for all tempest plugins. Only jobs
- # for the current releasable ("Maintained") stable branches should be listed
- # here.
+ # support the "Maintained" stable branches, so only jobs for the
+ # current stable branches should be listed here.
+ - cinder-tempest-plugin-basic-2024-1
- cinder-tempest-plugin-basic-2023-2
- cinder-tempest-plugin-basic-2023-1
- - cinder-tempest-plugin-basic-zed
- cinder-tempest-plugin-protection-functional
gate:
jobs:
@@ -38,9 +36,9 @@
- cinder-tempest-plugin-cbak-ceph
experimental:
jobs:
+ - cinder-tempest-plugin-cbak-ceph-2024-1
- cinder-tempest-plugin-cbak-ceph-2023-2
- cinder-tempest-plugin-cbak-ceph-2023-1
- - cinder-tempest-plugin-cbak-ceph-zed
- job:
name: cinder-tempest-plugin-protection-functional
@@ -269,6 +267,12 @@
timeout: 10800
- job:
+ name: cinder-tempest-plugin-cbak-ceph-2024-1
+ parent: cinder-tempest-plugin-cbak-ceph
+ nodeset: openstack-single-node-jammy
+ override-checkout: stable/2024.1
+
+- job:
name: cinder-tempest-plugin-cbak-ceph-2023-2
parent: cinder-tempest-plugin-cbak-ceph
nodeset: openstack-single-node-jammy
@@ -280,12 +284,6 @@
nodeset: openstack-single-node-jammy
override-checkout: stable/2023.1
-- job:
- name: cinder-tempest-plugin-cbak-ceph-zed
- parent: cinder-tempest-plugin-cbak-ceph
- nodeset: openstack-single-node-focal
- override-checkout: stable/zed
-
# variant for pre-Ussuri branches (no volume revert for Ceph),
# should this job be used on those branches
- job:
@@ -416,6 +414,12 @@
- ^releasenotes/.*$
- job:
+ name: cinder-tempest-plugin-basic-2024-1
+ parent: cinder-tempest-plugin-basic
+ nodeset: openstack-single-node-jammy
+ override-checkout: stable/2024.1
+
+- job:
name: cinder-tempest-plugin-basic-2023-2
parent: cinder-tempest-plugin-basic
nodeset: openstack-single-node-jammy
@@ -426,9 +430,3 @@
parent: cinder-tempest-plugin-basic
nodeset: openstack-single-node-jammy
override-checkout: stable/2023.1
-
-- job:
- name: cinder-tempest-plugin-basic-zed
- parent: cinder-tempest-plugin-basic
- nodeset: openstack-single-node-focal
- override-checkout: stable/zed
diff --git a/cinder_tempest_plugin/api/volume/base.py b/cinder_tempest_plugin/api/volume/base.py
index 1fd82bf..c0f53bd 100644
--- a/cinder_tempest_plugin/api/volume/base.py
+++ b/cinder_tempest_plugin/api/volume/base.py
@@ -13,6 +13,8 @@
# License for the specific language governing permissions and limitations
# under the License.
+import io
+
from tempest.common import compute
from tempest.common import waiters
from tempest import config
@@ -158,6 +160,29 @@
body['id'])
return body
+ @classmethod
+ def create_image_with_data(cls, **kwargs):
+ # we do this as a class method so we can use the
+ # addClassResourceCleanup functionality of tempest.test.BaseTestCase
+ images_client = cls.os_primary.image_client_v2
+ if 'min_disk' not in kwargs:
+ kwargs['min_disk'] = 1
+ response = images_client.create_image(**kwargs)
+ image_id = response['id']
+ cls.addClassResourceCleanup(
+ images_client.wait_for_resource_deletion, image_id)
+ cls.addClassResourceCleanup(
+ test_utils.call_and_ignore_notfound_exc,
+ images_client.delete_image, image_id)
+
+ # upload "data" to image
+ image_file = io.BytesIO(data_utils.random_bytes(size=1024))
+ images_client.store_image_file(image_id, image_file)
+
+ waiters.wait_for_image_status(images_client, image_id, 'active')
+ image = images_client.show_image(image_id)
+ return image
+
class BaseVolumeAdminTest(BaseVolumeTest):
"""Base test case class for all Volume Admin API tests."""
diff --git a/cinder_tempest_plugin/api/volume/test_create_from_image.py b/cinder_tempest_plugin/api/volume/test_create_from_image.py
index acb1943..f44f630 100644
--- a/cinder_tempest_plugin/api/volume/test_create_from_image.py
+++ b/cinder_tempest_plugin/api/volume/test_create_from_image.py
@@ -10,12 +10,7 @@
# License for the specific language governing permissions and limitations
# under the License.
-import io
-
-from tempest.common import waiters
from tempest import config
-from tempest.lib.common.utils import data_utils
-from tempest.lib.common.utils import test_utils
from tempest.lib import decorators
from cinder_tempest_plugin.api.volume import base
@@ -32,29 +27,6 @@
if not CONF.service_available.glance:
raise cls.skipException("Glance service is disabled")
- @classmethod
- def create_image_with_data(cls, **kwargs):
- # we do this as a class method so we can use the
- # addClassResourceCleanup functionality of tempest.test.BaseTestCase
- images_client = cls.os_primary.image_client_v2
- if 'min_disk' not in kwargs:
- kwargs['min_disk'] = 1
- response = images_client.create_image(**kwargs)
- image_id = response['id']
- cls.addClassResourceCleanup(
- images_client.wait_for_resource_deletion, image_id)
- cls.addClassResourceCleanup(
- test_utils.call_and_ignore_notfound_exc,
- images_client.delete_image, image_id)
-
- # upload "data" to image
- image_file = io.BytesIO(data_utils.random_bytes(size=1024))
- images_client.store_image_file(image_id, image_file)
-
- waiters.wait_for_image_status(images_client, image_id, 'active')
- image = images_client.show_image(image_id)
- return image
-
@decorators.idempotent_id('6e9266ff-a917-4dd5-aa4a-c36e59e7a2a6')
def test_create_from_image_with_volume_type_image_property(self):
"""Verify that the cinder_img_volume_type image property works.
diff --git a/cinder_tempest_plugin/api/volume/test_volume_dependency.py b/cinder_tempest_plugin/api/volume/test_volume_dependency.py
index b204e84..5ea067f 100644
--- a/cinder_tempest_plugin/api/volume/test_volume_dependency.py
+++ b/cinder_tempest_plugin/api/volume/test_volume_dependency.py
@@ -13,8 +13,12 @@
# License for the specific language governing permissions and limitations
# under the License.
+
+from tempest.common import utils
+from tempest.common import waiters
from tempest import config
from tempest.lib import decorators
+import testtools
from cinder_tempest_plugin.api.volume import base
@@ -119,3 +123,133 @@
self._delete_vol_and_wait(volume_1)
self._delete_vol_and_wait(volume_2)
self._delete_vol_and_wait(volume_3)
+
+
+class VolumeImageDependencyTests(base.BaseVolumeTest):
+ """Volume<->image dependency tests.
+
+ These tests perform clones to/from volumes and images,
+ deleting images/volumes that other volumes were cloned from.
+
+ Images and volumes are expected to be independent at the OpenStack
+ level, but in some configurations (i.e. when using Ceph as storage
+ for both Cinder and Glance) it was possible to end up with images
+ or volumes that could not be deleted. This was fixed for RBD in
+ Cinder 2024.1 change I009d0748f.
+
+ """
+
+ min_microversion = '3.40'
+
+ @classmethod
+ def del_image(cls, image_id):
+ images_client = cls.os_primary.image_client_v2
+ images_client.delete_image(image_id)
+ images_client.wait_for_resource_deletion(image_id)
+
+ @testtools.skipUnless(CONF.volume_feature_enabled.volume_image_dep_tests,
+ reason='Volume/image dependency tests not enabled.')
+ @utils.services('image', 'volume')
+ @decorators.idempotent_id('7a9fba78-2e4b-42b1-9898-bb4a60685320')
+ def test_image_volume_dependencies_1(self):
+ # image -> volume
+ image_args = {
+ 'disk_format': 'raw',
+ 'container_format': 'bare',
+ 'name': 'image-for-test-7a9fba78-2e4b-42b1-9898-bb4a60685320'
+ }
+ image = self.create_image_with_data(**image_args)
+
+ # create a volume from the image
+ vol_args = {'name': ('volume1-for-test'
+ '7a9fba78-2e4b-42b1-9898-bb4a60685320'),
+ 'imageRef': image['id']}
+ volume1 = self.create_volume(**vol_args)
+ waiters.wait_for_volume_resource_status(self.volumes_client,
+ volume1['id'],
+ 'available')
+
+ self.volumes_client.delete_volume(volume1['id'])
+ self.volumes_client.wait_for_resource_deletion(volume1['id'])
+
+ self.del_image(image['id'])
+
+ @testtools.skipUnless(CONF.volume_feature_enabled.volume_image_dep_tests,
+ reason='Volume/image dependency tests not enabled.')
+ @utils.services('image', 'volume')
+ @decorators.idempotent_id('0e20bd6e-440f-41d8-9b5d-fc047ac00423')
+ def test_image_volume_dependencies_2(self):
+ # image -> volume -> volume
+
+ image_args = {
+ 'disk_format': 'raw',
+ 'container_format': 'bare',
+ 'name': 'image-for-test-0e20bd6e-440f-41d8-9b5d-fc047ac00423'
+ }
+ image = self.create_image_with_data(**image_args)
+
+ # create a volume from the image
+ vol_args = {'name': ('volume1-for-test'
+ '0e20bd6e-440f-41d8-9b5d-fc047ac00423'),
+ 'imageRef': image['id']}
+ volume1 = self.create_volume(**vol_args)
+ waiters.wait_for_volume_resource_status(self.volumes_client,
+ volume1['id'],
+ 'available')
+
+ vol2_args = {'name': ('volume2-for-test-'
+ '0e20bd6e-440f-41d8-9b5d-fc047ac00423'),
+ 'source_volid': volume1['id']}
+ volume2 = self.create_volume(**vol2_args)
+ waiters.wait_for_volume_resource_status(self.volumes_client,
+ volume2['id'],
+ 'available')
+
+ self.volumes_client.delete_volume(volume1['id'])
+ self.volumes_client.wait_for_resource_deletion(volume1['id'])
+
+ self.del_image(image['id'])
+
+ @testtools.skipUnless(CONF.volume_feature_enabled.volume_image_dep_tests,
+ reason='Volume/image dependency tests not enabled.')
+ @decorators.idempotent_id('e6050452-06bd-4c7f-9912-45178c83e379')
+ @utils.services('image', 'volume')
+ def test_image_volume_dependencies_3(self):
+ # image -> volume -> snap -> volume
+
+ image_args = {
+ 'disk_format': 'raw',
+ 'container_format': 'bare',
+ 'name': 'image-for-test-e6050452-06bd-4c7f-9912-45178c83e379'
+ }
+ image = self.create_image_with_data(**image_args)
+
+ # create a volume from the image
+ vol_args = {'name': ('volume1-for-test'
+ 'e6050452-06bd-4c7f-9912-45178c83e379'),
+ 'imageRef': image['id']}
+ volume1 = self.create_volume(**vol_args)
+ waiters.wait_for_volume_resource_status(self.volumes_client,
+ volume1['id'],
+ 'available')
+
+ snapshot1 = self.create_snapshot(volume1['id'])
+
+ vol2_args = {'name': ('volume2-for-test-'
+ 'e6050452-06bd-4c7f-9912-45178c83e379'),
+ 'snapshot_id': snapshot1['id']}
+ volume2 = self.create_volume(**vol2_args)
+ waiters.wait_for_volume_resource_status(self.volumes_client,
+ volume2['id'],
+ 'available')
+
+ self.snapshots_client.delete_snapshot(snapshot1['id'])
+ self.snapshots_client.wait_for_resource_deletion(snapshot1['id'])
+
+ self.volumes_client.delete_volume(volume2['id'])
+ self.volumes_client.wait_for_resource_deletion(volume2['id'])
+
+ self.del_image(image['id'])
+
+ self.volumes_client.delete_volume(volume1['id'])
+ self.volumes_client.wait_for_resource_deletion(volume1['id'])
diff --git a/cinder_tempest_plugin/config.py b/cinder_tempest_plugin/config.py
index 78dd6ea..53222b8 100644
--- a/cinder_tempest_plugin/config.py
+++ b/cinder_tempest_plugin/config.py
@@ -22,6 +22,9 @@
cfg.BoolOpt('volume_revert',
default=False,
help='Enable to run Cinder volume revert tests'),
+ cfg.BoolOpt('volume_image_dep_tests',
+ default=True,
+ help='Run tests for dependencies between images and volumes')
]
# The barbican service is discovered by config_tempest [1], and will appear
diff --git a/setup.cfg b/setup.cfg
index f224c5c..3d74cb9 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -20,6 +20,7 @@
Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9
Programming Language :: Python :: 3.10
+ Programming Language :: Python :: 3.11
[files]
packages =