diff options
21 files changed, 293 insertions, 51 deletions
diff --git a/.zuul.yaml b/.zuul.yaml index 887e5e399..71739fa81 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -63,6 +63,11 @@ irrelevant-files: *gate-irrelevant-files - tempest-ipv6-only: irrelevant-files: *gate-irrelevant-files + - openstacksdk-functional-devstack: + required-projects: + - name: opendev.org/openstack/openstacksdk + override-branch: stable/ussuri + irrelevant-files: *gate-irrelevant-files gate: jobs: - cinder-grenade-mn-sub-volbak: @@ -75,6 +80,11 @@ irrelevant-files: *gate-irrelevant-files - tempest-ipv6-only: irrelevant-files: *gate-irrelevant-files + - openstacksdk-functional-devstack: + required-projects: + - name: opendev.org/openstack/openstacksdk + override-branch: stable/ussuri + irrelevant-files: *gate-irrelevant-files experimental: jobs: - tempest-cinder-v2-api: @@ -121,16 +131,7 @@ - job: name: cinder-plugin-ceph-tempest parent: devstack-plugin-ceph-tempest-py3 - roles: - - zuul: opendev.org/openstack/cinderlib - - zuul: opendev.org/openstack/cinder-tempest-plugin - run: playbooks/tempest-and-cinderlib-run.yaml - post-run: playbooks/post-cinderlib.yaml - required-projects: - - opendev.org/openstack/cinderlib vars: - zuul_additional_subunit_dirs: - - "{{ ansible_user_dir }}/{{ zuul.projects['opendev.org/openstack/cinderlib'].src_dir }}" devstack_local_conf: test-config: $TEMPEST_CONFIG: @@ -220,7 +221,8 @@ * legacy-tempest-dsvm-lvm-multibackend timeout: 10800 required-projects: - - opendev.org/openstack/cinder-tempest-plugin + - name: opendev.org/openstack/cinder-tempest-plugin + override-checkout: 1.3.0 vars: tox_envlist: all tempest_test_regex: '(?!.*\[.*\bslow\b.*\])(^tempest\.(api|scenario)|(^cinder_tempest_plugin))' diff --git a/api-ref/source/v3/parameters.yaml b/api-ref/source/v3/parameters.yaml index 52a8182c0..15dfecd57 100644 --- a/api-ref/source/v3/parameters.yaml +++ b/api-ref/source/v3/parameters.yaml @@ -850,6 +850,13 @@ container_format: in: body required: false type: string +container_format_upload: + description: | + Container format for the new image. Default is bare. (Note: Volumes + of an encrypted volume type must use a bare container format.) + in: body + required: false + type: string control_location: description: | Notional service where encryption is performed. Valid values are @@ -1097,6 +1104,13 @@ disk_format: in: body required: false type: string +disk_format_upload: + description: | + Disk format for the new image. Default is raw. (Note: volumes of an + encrypted volume type can only be uploaded in raw format.) + in: body + required: false + type: string display_name: description: | The name of volume backend capabilities. diff --git a/api-ref/source/v3/volumes-v3-volumes-actions.inc b/api-ref/source/v3/volumes-v3-volumes-actions.inc index 36774e318..0c73f7880 100644 --- a/api-ref/source/v3/volumes-v3-volumes-actions.inc +++ b/api-ref/source/v3/volumes-v3-volumes-actions.inc @@ -694,8 +694,8 @@ Request - os-volume_upload_image: os-volume_upload_image - image_name: image_name - force: force_upload_vol - - disk_format: disk_format - - container_format: container_format + - disk_format: disk_format_upload + - container_format: container_format_upload - visibility: visibility_min - protected: protected diff --git a/cinder/api/contrib/volume_actions.py b/cinder/api/contrib/volume_actions.py index 0db41c592..97d45728c 100644 --- a/cinder/api/contrib/volume_actions.py +++ b/cinder/api/contrib/volume_actions.py @@ -219,6 +219,14 @@ class VolumeActionsController(wsgi.Controller): "name": params["image_name"]} if volume.encryption_key_id: + # encrypted volumes cannot be converted on upload + if (image_metadata['disk_format'] != 'raw' + or image_metadata['container_format'] != 'bare'): + msg = _("An encrypted volume uploaded as an image must use " + "'raw' disk_format and 'bare' container_format, " + "which are the defaults for these options.") + raise webob.exc.HTTPBadRequest(explanation=msg) + # Clone volume encryption key: the current key cannot # be reused because it will be deleted when the volume is # deleted. diff --git a/cinder/backup/api.py b/cinder/backup/api.py index 7e03348af..30a036c23 100644 --- a/cinder/backup/api.py +++ b/cinder/backup/api.py @@ -499,6 +499,23 @@ class API(base.Base): msg = _('Provided backup record is missing size attribute') raise exception.InvalidInput(reason=msg) + # Try to get the backup with that ID in all projects even among + # deleted entries (we reuse soft-deleted backups). + try: + backup = objects.BackupImport.get_by_id( + context.elevated(read_deleted='yes'), + backup_record['id'], + project_only=False) + + # If record exists and it's not deleted we cannot proceed + # with the import + if backup.status != fields.BackupStatus.DELETED: + msg = _('Backup already exists in database.') + raise exception.InvalidBackup(reason=msg) + except exception.BackupNotFound: + pass + + # Check that we're under limit by reserving quota try: reserve_opts = {'backups': 1, 'backup_gigabytes': backup_record['size']} @@ -520,30 +537,16 @@ class API(base.Base): } try: - try: - # Try to get the backup with that ID in all projects even among - # deleted entries. - backup = objects.BackupImport.get_by_id( - context.elevated(read_deleted='yes'), - backup_record['id'], - project_only=False) - - # If record exists and it's not deleted we cannot proceed - # with the import - if backup.status != fields.BackupStatus.DELETED: - msg = _('Backup already exists in database.') - raise exception.InvalidBackup(reason=msg) - - # Otherwise we'll "revive" delete backup record + if backup: + # "revive" the soft-deleted backup record retrieved earlier backup.update(kwargs) backup.save() - QUOTAS.commit(context, reservations) - except exception.BackupNotFound: - # If record doesn't exist create it with the specific ID + else: + # create a new backup with the specified ID backup = objects.BackupImport(context=context, id=backup_record['id'], **kwargs) backup.create() - QUOTAS.commit(context, reservations) + QUOTAS.commit(context, reservations) except Exception: with excutils.save_and_reraise_exception(): try: diff --git a/cinder/tests/unit/api/contrib/test_backups.py b/cinder/tests/unit/api/contrib/test_backups.py index b8b247967..07184fdc8 100644 --- a/cinder/tests/unit/api/contrib/test_backups.py +++ b/cinder/tests/unit/api/contrib/test_backups.py @@ -2242,9 +2242,14 @@ class BackupsAPITestCase(test.TestCase): res_dict['badRequest']['code']) self.assertEqual('Invalid backup: Backup already exists in database.', res_dict['badRequest']['message']) - mock_reserve.assert_called_with( - ctx, backups=1, backup_gigabytes=1) - mock_rollback.assert_called_with(ctx, "fake_reservation") + + # Bug #1965847: already existing backup should not be deleted + self.assertNotEqual(fields.BackupStatus.DELETED, + self._get_backup_attrib(backup.id, 'status')) + mock_reserve.assert_not_called() + mock_rollback.assert_not_called() + mock_commit.assert_not_called() + backup.destroy() @mock.patch.object(quota.QUOTAS, 'commit') diff --git a/cinder/tests/unit/api/contrib/test_volume_actions.py b/cinder/tests/unit/api/contrib/test_volume_actions.py index 5651152c8..9d0e3ce3b 100644 --- a/cinder/tests/unit/api/contrib/test_volume_actions.py +++ b/cinder/tests/unit/api/contrib/test_volume_actions.py @@ -1006,6 +1006,38 @@ class VolumeImageActionsTest(test.TestCase): id, body=body) + @mock.patch.object(volume_api.API, 'get', fake_volume_get_obj) + def test_copy_volume_to_image_bad_disk_format_for_encrypted_vol(self): + id = ENCRYPTED_VOLUME_ID + vol = {"container_format": 'bare', + "disk_format": 'qcow2', + "image_name": 'image_name', + "force": True} + body = {"os-volume_upload_image": vol} + req = fakes.HTTPRequest.blank('/v3/%s/volumes/%s/action' + % (fake.PROJECT_ID, id)) + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller._volume_upload_image, + req, + id, + body=body) + + @mock.patch.object(volume_api.API, 'get', fake_volume_get_obj) + def test_copy_volume_to_image_bad_container_format_for_encrypted_vol(self): + id = ENCRYPTED_VOLUME_ID + vol = {"container_format": 'ovf', + "disk_format": 'raw', + "image_name": 'image_name', + "force": True} + body = {"os-volume_upload_image": vol} + req = fakes.HTTPRequest.blank('/v3/%s/volumes/%s/action' + % (fake.PROJECT_ID, id)) + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller._volume_upload_image, + req, + id, + body=body) + @mock.patch.object(volume_api.API, "copy_volume_to_image") def test_copy_volume_to_image_disk_format_ploop(self, mock_copy_to_image): diff --git a/cinder/tests/unit/backup/test_backup.py b/cinder/tests/unit/backup/test_backup.py index 8b5d85154..24b725c86 100644 --- a/cinder/tests/unit/backup/test_backup.py +++ b/cinder/tests/unit/backup/test_backup.py @@ -2050,13 +2050,86 @@ class BackupAPITestCase(BaseBackupTest): 'user_id': backup.user_id, 'volume_id': backup.volume_id, 'size': 1} - mock_reserve.return_value = 'fake_reservation' self.assertRaises(exception.InvalidBackup, self.api._get_import_backup, self.ctxt, 'fake_backup_url') + + # make sure Bug #1965847 has been fixed + backup = db.backup_get(self.ctxt, backup.id) + self.assertNotEqual(fields.BackupStatus.DELETED, backup.status) + + # the fix for Bug #1965847 changed the workflow in the method + # under test, so we check that none of this stuff happens any more + mock_reserve.assert_not_called() + mock_rollback.assert_not_called() + mock_commit.assert_not_called() + + @mock.patch.object(objects.Backup, 'decode_record') + @mock.patch.object(quota.QUOTAS, 'commit') + @mock.patch.object(quota.QUOTAS, 'rollback') + @mock.patch.object(quota.QUOTAS, 'reserve') + def test__get_import_backup_reuse_backup( + self, mock_reserve, mock_rollback, mock_commit, mock_decode): + backup = self._create_backup_db_entry( + size=1, + status=fields.BackupStatus.DELETED) + mock_decode.return_value = {'id': backup.id, + 'project_id': backup.project_id, + 'user_id': backup.user_id, + 'volume_id': backup.volume_id, + 'size': 1} + mock_reserve.return_value = 'fake_reservation' + self.ctxt.user_id = 'fake_user' + self.ctxt.project_id = 'fake_project' + + # check pre-conditions + self.assertNotEqual(self.ctxt.user_id, backup.user_id) + self.assertNotEqual(self.ctxt.project_id, backup.project_id) + self.assertEqual(fields.BackupStatus.DELETED, backup.status) + + self.api._get_import_backup(self.ctxt, 'fake_backup_url') + + # check post-conditions + backup = db.backup_get(self.ctxt, backup.id) + self.assertEqual(self.ctxt.user_id, backup.user_id) + self.assertEqual(self.ctxt.project_id, backup.project_id) + self.assertNotEqual(fields.BackupStatus.DELETED, backup.status) + + mock_reserve.assert_called_with( + self.ctxt, backups=1, backup_gigabytes=1) + mock_commit.assert_called_with(self.ctxt, 'fake_reservation') + mock_rollback.assert_not_called() + + @mock.patch.object(objects.BackupImport, '__init__') + @mock.patch.object(objects.BackupImport, 'get_by_id') + @mock.patch.object(objects.Backup, 'decode_record') + @mock.patch.object(quota.QUOTAS, 'commit') + @mock.patch.object(quota.QUOTAS, 'rollback') + @mock.patch.object(quota.QUOTAS, 'reserve') + def test__get_import_backup_rollback_situation( + self, mock_reserve, mock_rollback, mock_commit, mock_decode, + mock_get_by_id, mock_imp_init): + mock_decode.return_value = {'id': fake.BACKUP_ID, + 'project_id': fake.PROJECT_ID, + 'user_id': fake.USER_ID, + 'volume_id': fake.VOLUME_ID, + 'size': 1} + # we won't find a backup, so we'll need to create one + mock_get_by_id.side_effect = exception.BackupNotFound( + backup_id=fake.BACKUP_ID) + # we should make a reservation ... + mock_reserve.return_value = 'fake_reservation' + # ... but will fail to create and will have to roll back + mock_imp_init.side_effect = FakeBackupException, + + self.assertRaises(FakeBackupException, + self.api._get_import_backup, + self.ctxt, 'fake_backup_url') + mock_reserve.assert_called_with( self.ctxt, backups=1, backup_gigabytes=1) + mock_commit.assert_not_called() mock_rollback.assert_called_with(self.ctxt, "fake_reservation") @mock.patch('cinder.objects.BackupList.get_all_by_volume') diff --git a/cinder/tests/unit/volume/drivers/dell_emc/powermax/test_powermax_rest.py b/cinder/tests/unit/volume/drivers/dell_emc/powermax/test_powermax_rest.py index a093d16cf..05bae99e5 100644 --- a/cinder/tests/unit/volume/drivers/dell_emc/powermax/test_powermax_rest.py +++ b/cinder/tests/unit/volume/drivers/dell_emc/powermax/test_powermax_rest.py @@ -601,6 +601,22 @@ class PowerMaxRestTest(test.TestCase): self.assertTrue(is_child1) self.assertFalse(is_child2) + def test_is_child_sg_in_parent_sg_case_not_matching(self): + lower_case_host = 'OS-hostx-SRP_1-DiamondDSS-os-fibre-PG' + + is_child1 = self.rest.is_child_sg_in_parent_sg( + self.data.array, lower_case_host, + self.data.parent_sg_f) + self.assertTrue(is_child1) + + def test_is_child_sg_in_parent_sg_spelling_mistake(self): + lower_case_host = 'OS-hosty-SRP_1-DiamondDSS-os-fiber-PG' + + is_child1 = self.rest.is_child_sg_in_parent_sg( + self.data.array, lower_case_host, + self.data.parent_sg_f) + self.assertFalse(is_child1) + def test_add_child_sg_to_parent_sg(self): payload = {'editStorageGroupActionParam': { 'expandStorageGroupParam': { diff --git a/cinder/tests/unit/volume/drivers/hpe/test_hpe3par.py b/cinder/tests/unit/volume/drivers/hpe/test_hpe3par.py index 4972c7967..060e4537e 100644 --- a/cinder/tests/unit/volume/drivers/hpe/test_hpe3par.py +++ b/cinder/tests/unit/volume/drivers/hpe/test_hpe3par.py @@ -763,14 +763,15 @@ class HPE3PARBaseDriver(test.TestCase): spec=True, ) def setup_mock_client(self, _m_client, driver, conf=None, m_conf=None, - is_primera=False): + is_primera=False, + wsapi_version=wsapi_version_latest): _m_client = _m_client.return_value # Configure the base constants, defaults etc... _m_client.configure_mock(**self.mock_client_conf) - _m_client.getWsApiVersion.return_value = self.wsapi_version_latest + _m_client.getWsApiVersion.return_value = wsapi_version _m_client.is_primera_array.return_value = is_primera @@ -8909,10 +8910,49 @@ class TestHPE3PARISCSIDriver(HPE3PARBaseDriver): return mock_client - def test_iscsi_primera(self): + def test_iscsi_primera_old(self): + # primera 4.0.xx.yyy + wsapi_version_primera_old = {'major': 1, + 'build': 40000128, + 'minor': 8, + 'revision': 1} + self.assertRaises(NotImplementedError, self.setup_mock_client, driver=hpedriver.HPE3PARISCSIDriver, - is_primera=True) + is_primera=True, + wsapi_version=wsapi_version_primera_old) + + def test_iscsi_primera_new(self, config=None, mock_conf=None): + # primera 4.2.xx.yyy + wsapi_version_primera_new = {'major': 1, + 'build': 40202010, + 'minor': 8, + 'revision': 1} + + self.ctxt = context.get_admin_context() + + mock_client = self.setup_mock_client( + conf=config, + m_conf=mock_conf, + driver=hpedriver.HPE3PARISCSIDriver, + is_primera=True, + wsapi_version=wsapi_version_primera_new) + + expected_get_cpgs = [ + mock.call.getCPG(HPE3PAR_CPG), + mock.call.getCPG(HPE3PAR_CPG2)] + expected_get_ports = [mock.call.getPorts()] + expected_primera = [ + mock.call.is_primera_array(), + mock.call.getWsApiVersion()] + mock_client.assert_has_calls( + self.standard_login + + expected_get_cpgs + + self.standard_logout + + expected_primera + + self.standard_login + + expected_get_ports + + self.standard_logout) @ddt.data('volume', 'volume_name_id') def test_initialize_connection(self, volume_attr): diff --git a/cinder/volume/drivers/datera/datera_common.py b/cinder/volume/drivers/datera/datera_common.py index a9ec14065..b60af66ee 100644 --- a/cinder/volume/drivers/datera/datera_common.py +++ b/cinder/volume/drivers/datera/datera_common.py @@ -285,7 +285,7 @@ def create_tenant(driver, project_id): try: driver.api.tenants.create(name=name) except dfs_sdk.exceptions.ApiConflictError: - LOG.debug("Tenant {} already exists".format(name)) + LOG.debug("Tenant %s already exists", name) return _format_tenant(name) diff --git a/cinder/volume/drivers/dell_emc/powermax/fc.py b/cinder/volume/drivers/dell_emc/powermax/fc.py index 2af3b2cd2..54017da37 100644 --- a/cinder/volume/drivers/dell_emc/powermax/fc.py +++ b/cinder/volume/drivers/dell_emc/powermax/fc.py @@ -134,9 +134,10 @@ class PowerMaxFCDriver(san.SanDriver, driver.FibreChannelDriver): 4.2.10 - Replica RDFG suspend fix (#1892718) 4.2.11 - Fix non-temporary snapshot delete (#1887962) 4.2.12 - Fix for create snapshot (#1939139) + 4.2.13 - Allow for case mismatch in SGs (bug #1929429) """ - VERSION = "4.2.12" + VERSION = "4.2.13" # ThirdPartySystems wiki CI_WIKI_NAME = "EMC_VMAX_CI" diff --git a/cinder/volume/drivers/dell_emc/powermax/iscsi.py b/cinder/volume/drivers/dell_emc/powermax/iscsi.py index 6565293b8..24abd7777 100644 --- a/cinder/volume/drivers/dell_emc/powermax/iscsi.py +++ b/cinder/volume/drivers/dell_emc/powermax/iscsi.py @@ -139,9 +139,10 @@ class PowerMaxISCSIDriver(san.SanISCSIDriver): 4.2.10 - Replica RDFG suspend fix (#1892718) 4.2.11 - Fix non-temporary snapshot delete (#1887962) 4.2.12 - Fix for create snapshot (#1939139) + 4.2.13 - Allow for case mismatch in SGs (bug #1929429) """ - VERSION = "4.2.12" + VERSION = "4.2.13" # ThirdPartySystems wiki CI_WIKI_NAME = "EMC_VMAX_CI" diff --git a/cinder/volume/drivers/dell_emc/powermax/rest.py b/cinder/volume/drivers/dell_emc/powermax/rest.py index f8b0a25ba..bf5330f4e 100644 --- a/cinder/volume/drivers/dell_emc/powermax/rest.py +++ b/cinder/volume/drivers/dell_emc/powermax/rest.py @@ -773,7 +773,8 @@ class PowerMaxRest(object): parent_sg = self.get_storage_group(array, parent_name) if parent_sg and parent_sg.get('child_storage_group'): child_sg_list = parent_sg['child_storage_group'] - if child_name in child_sg_list: + if child_name.lower() in ( + child.lower() for child in child_sg_list): return True return False diff --git a/cinder/volume/drivers/hpe/hpe_3par_iscsi.py b/cinder/volume/drivers/hpe/hpe_3par_iscsi.py index 83febae89..0d809e048 100644 --- a/cinder/volume/drivers/hpe/hpe_3par_iscsi.py +++ b/cinder/volume/drivers/hpe/hpe_3par_iscsi.py @@ -128,10 +128,11 @@ class HPE3PARISCSIDriver(hpebasedriver.HPE3PARDriverBase): failover. bug #1773069 4.0.4 - Added Peer Persistence feature 4.0.5 - Added Primera array check. bug #1849525 + 4.0.6 - Allow iSCSI support for Primera 4.2 onwards """ - VERSION = "4.0.5" + VERSION = "4.0.6" # The name of the CI wiki page. CI_WIKI_NAME = "HPE_Storage_CI" @@ -144,9 +145,17 @@ class HPE3PARISCSIDriver(hpebasedriver.HPE3PARDriverBase): client_obj = common.client is_primera = client_obj.is_primera_array() if is_primera: - LOG.error("For Primera, only FC is supported. " - "iSCSI cannot be used") - raise NotImplementedError() + api_version = client_obj.getWsApiVersion() + array_version = api_version['build'] + LOG.debug("array version: %(version)s", + {'version': array_version}) + if array_version < 40200000: + err_msg = (_('The iSCSI driver is not supported for ' + 'Primera %(version)s. It is supported ' + 'for Primera 4.2 or higher versions.') + % {'version': array_version}) + LOG.error(err_msg) + raise NotImplementedError() self.iscsi_ips = {} common.client_login() diff --git a/doc/source/configuration/block-storage/drivers/hpe-3par-driver.rst b/doc/source/configuration/block-storage/drivers/hpe-3par-driver.rst index d013cdc03..bad3e3d4c 100644 --- a/doc/source/configuration/block-storage/drivers/hpe-3par-driver.rst +++ b/doc/source/configuration/block-storage/drivers/hpe-3par-driver.rst @@ -378,15 +378,15 @@ OpenStack software. san_password=3parpass # FIBRE CHANNEL DRIVER - # Note: For Primera, only FC driver is supported as of now. # (uncomment the next line to enable the FC driver) #volume_driver=cinder.volume.drivers.hpe.hpe_3par_fc.HPE3PARFCDriver # iSCSI DRIVER # If you enable the iSCSI driver, you must also set values # for hpe3par_iscsi_ips or iscsi_ip_address in this file. - # Note: Primera currently requires the FC driver. If you - # configure iSCSI with Primera, the driver will fail to start. + # Note: The iSCSI driver is supported with 3PAR (all versions) + # and Primera (version 4.2 or higher). If you configure iSCSI + # with Primera 4.0 or 4.1, the driver will fail to start. # (uncomment the next line to enable the iSCSI driver) #volume_driver=cinder.volume.drivers.hpe.hpe_3par_iscsi.HPE3PARISCSIDriver diff --git a/playbooks/tempest-and-cinderlib-run.yaml b/playbooks/tempest-and-cinderlib-run.yaml index cbb33273d..e71417d2b 100644 --- a/playbooks/tempest-and-cinderlib-run.yaml +++ b/playbooks/tempest-and-cinderlib-run.yaml @@ -19,7 +19,7 @@ - setup-tempest-data-dir - acl-devstack-files - role: run-tempest - # ignore the errors, so that run-cinderlib-tests is always executed + # ignore the errors here (but consider them later), so that run-cinderlib-tests is always executed ignore_errors: yes - role: change-devstack-data-owner devstack_data_subdir_changed: cinder @@ -27,3 +27,8 @@ - role: run-cinderlib-tests tox_install_siblings: false cinderlib_base_dir: "{{ ansible_user_dir }}/{{ zuul.projects['opendev.org/openstack/cinderlib'].src_dir }}" + post_tasks: + - name: Fail if the first tempest run did not work + fail: + msg: "tempest run returned with an error" + when: tempest_run_result is defined and tempest_run_result.rc != 0 diff --git a/releasenotes/notes/bug-193688-bb045badcd5aecad.yaml b/releasenotes/notes/bug-193688-bb045badcd5aecad.yaml new file mode 100644 index 000000000..f4a141dba --- /dev/null +++ b/releasenotes/notes/bug-193688-bb045badcd5aecad.yaml @@ -0,0 +1,12 @@ +--- +fixes: + - | + `Bug #1935688 <https://bugs.launchpad.net/cinder/+bug/1935688>`_: + Cinder only supports uploading a volume of an encrypted volume type as an + image to the Image service in ``raw`` format using a ``bare`` container + type. Previously, ``os-volume_upload_image`` action requests to the Block + Storage API specifying different format option values were accepted, but + would result in a later failure. This condition is now checked at the API + layer, and ``os-volume_upload_image`` action requests on a volume of an + encrypted type that specify unsupported values for ``disk_format`` or + ``container_format`` now result in a 400 (Bad Request) response. diff --git a/releasenotes/notes/bug-1965847-fix-backup-import-3b3ccdf740a13cff.yaml b/releasenotes/notes/bug-1965847-fix-backup-import-3b3ccdf740a13cff.yaml new file mode 100644 index 000000000..b51efd3a8 --- /dev/null +++ b/releasenotes/notes/bug-1965847-fix-backup-import-3b3ccdf740a13cff.yaml @@ -0,0 +1,7 @@ +--- +fixes: + - | + `Bug #1965847 <https://bugs.launchpad.net/cinder/+bug/1965847>`_: Fixed + issue where importing a backup record for a backup_id that currently + existed had the unfortunate side effect of deleting the existing backup + record. diff --git a/releasenotes/notes/bug1929429-e749f5e5a242a599.yaml b/releasenotes/notes/bug1929429-e749f5e5a242a599.yaml new file mode 100644 index 000000000..d7a7e3d6e --- /dev/null +++ b/releasenotes/notes/bug1929429-e749f5e5a242a599.yaml @@ -0,0 +1,8 @@ +--- +fixes: + - | + PowerMax driver `bug #1929429 + <https://bugs.launchpad.net/cinder/+bug/1929429>`_: Fixes + child/parent storage group check so that a pattern match + is not case sensitive. For example, myStorageGroup should + equal MYSTORAGEGROUP and mystoragegroup. diff --git a/releasenotes/notes/hpe-3par-primera-add-iscsi-5af339643dfa0928.yaml b/releasenotes/notes/hpe-3par-primera-add-iscsi-5af339643dfa0928.yaml new file mode 100644 index 000000000..03166024b --- /dev/null +++ b/releasenotes/notes/hpe-3par-primera-add-iscsi-5af339643dfa0928.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + HPE 3PAR Driver: Add support of iSCSI driver for Primera 4.2 + or higher versions. |