From c1007ed82760851d0bd1ad60a8b5351e1958e000 Mon Sep 17 00:00:00 2001 From: "Jay S. Bryant" Date: Fri, 28 Sep 2018 18:13:32 -0500 Subject: [PATCH] Remove the ITRI DISCO driver The ITRI DISCO driver was marked unsupported in the Rocky release. It hasn't reported on a patch in 74 days and hasn't successfully executed CI in 231 days. This falls well outside our CI requirements and therefore the driver is being removed. Change-Id: Iab4288983e8e13a3b9d59b30887d1934a2eb66c8 --- cinder/opts.py | 3 - .../unit/volume/drivers/disco/__init__.py | 163 ----- .../disco/test_create_cloned_volume.py | 158 ----- .../drivers/disco/test_create_snapshot.py | 153 ----- .../drivers/disco/test_create_volume.py | 53 -- .../disco/test_create_volume_from_snapshot.py | 166 ----- .../drivers/disco/test_delete_snapshot.py | 51 -- .../drivers/disco/test_delete_volume.py | 49 -- .../drivers/disco/test_extend_volume.py | 50 -- .../drivers/disco/test_manage_existing.py | 133 ---- cinder/volume/drivers/disco/__init__.py | 0 cinder/volume/drivers/disco/disco.py | 650 ------------------ cinder/volume/drivers/disco/disco_api.py | 165 ----- .../drivers/disco/disco_attach_detach.py | 69 -- .../drivers/itri-disco-driver.rst | 27 - .../block-storage/volume-drivers.rst | 1 - doc/source/reference/support-matrix.ini | 12 - doc/source/reference/support-matrix.rst | 1 + ...disco-driver-removal-11e14fbf431ea876.yaml | 15 + 19 files changed, 16 insertions(+), 1903 deletions(-) delete mode 100644 cinder/tests/unit/volume/drivers/disco/__init__.py delete mode 100644 cinder/tests/unit/volume/drivers/disco/test_create_cloned_volume.py delete mode 100644 cinder/tests/unit/volume/drivers/disco/test_create_snapshot.py delete mode 100644 cinder/tests/unit/volume/drivers/disco/test_create_volume.py delete mode 100644 cinder/tests/unit/volume/drivers/disco/test_create_volume_from_snapshot.py delete mode 100644 cinder/tests/unit/volume/drivers/disco/test_delete_snapshot.py delete mode 100644 cinder/tests/unit/volume/drivers/disco/test_delete_volume.py delete mode 100644 cinder/tests/unit/volume/drivers/disco/test_extend_volume.py delete mode 100644 cinder/tests/unit/volume/drivers/disco/test_manage_existing.py delete mode 100644 cinder/volume/drivers/disco/__init__.py delete mode 100644 cinder/volume/drivers/disco/disco.py delete mode 100644 cinder/volume/drivers/disco/disco_api.py delete mode 100644 cinder/volume/drivers/disco/disco_attach_detach.py delete mode 100644 doc/source/configuration/block-storage/drivers/itri-disco-driver.rst create mode 100644 releasenotes/notes/itri-disco-driver-removal-11e14fbf431ea876.yaml diff --git a/cinder/opts.py b/cinder/opts.py index ea4c3acef6e..653fb40d6e6 100644 --- a/cinder/opts.py +++ b/cinder/opts.py @@ -91,8 +91,6 @@ from cinder.volume.drivers.dell_emc.vnx import common as \ cinder_volume_drivers_dell_emc_vnx_common from cinder.volume.drivers.dell_emc import xtremio as \ cinder_volume_drivers_dell_emc_xtremio -from cinder.volume.drivers.disco import disco as \ - cinder_volume_drivers_disco_disco from cinder.volume.drivers import drbdmanagedrv as \ cinder_volume_drivers_drbdmanagedrv from cinder.volume.drivers.fujitsu import eternus_dx_common as \ @@ -287,7 +285,6 @@ def list_opts(): cinder_volume_drivers_dell_emc_vmax_common.vmax_opts, cinder_volume_drivers_dell_emc_vnx_common.VNX_OPTS, cinder_volume_drivers_dell_emc_xtremio.XTREMIO_OPTS, - cinder_volume_drivers_disco_disco.disco_opts, cinder_volume_drivers_drbdmanagedrv.drbd_opts, cinder_volume_drivers_fujitsu_eternusdxcommon. FJ_ETERNUS_DX_OPT_opts, diff --git a/cinder/tests/unit/volume/drivers/disco/__init__.py b/cinder/tests/unit/volume/drivers/disco/__init__.py deleted file mode 100644 index 8460126e062..00000000000 --- a/cinder/tests/unit/volume/drivers/disco/__init__.py +++ /dev/null @@ -1,163 +0,0 @@ -# Copyright (c) 2015 Industrial Technology Research Institute. -# -# 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. - -"""Parent class for the DISCO driver unit test.""" - -import mock -from suds import client - -from os_brick.initiator import connector - -from cinder import context -from cinder import test -from cinder.tests.unit import fake_volume -from cinder.volume import configuration as conf -import cinder.volume.drivers.disco.disco as driver -import cinder.volume.drivers.disco.disco_api as disco_api -import cinder.volume.drivers.disco.disco_attach_detach as attach_detach - - -class TestDISCODriver(test.TestCase): - """Generic class for the DISCO test case.""" - - DETAIL_OPTIONS = { - 'success': 1, - 'pending': 2, - 'failure': 3 - } - - ERROR_STATUS = 1 - - def setUp(self): - """Initialise variable common to all the test cases.""" - super(TestDISCODriver, self).setUp() - - mock_exec = mock.Mock() - mock_exec.return_value = ('', '') - self.cfg = mock.Mock(spec=conf.Configuration) - self.cfg.disco_client = '127.0.0.1' - self.cfg.disco_client_port = '9898' - self.cfg.disco_wsdl_path = 'somewhere' - self.cfg.disco_volume_name_prefix = 'openstack-' - self.cfg.disco_snapshot_check_timeout = 3600 - self.cfg.disco_restore_check_timeout = 3600 - self.cfg.disco_clone_check_timeout = 3600 - self.cfg.disco_retry_interval = 2 - self.cfg.num_volume_device_scan_tries = 3 - self.cfg.disco_choice_client = 'SOAP' - self.cfg.disco_rest_ip = '127.0.0.1' - self.cfg.san_ip = '127.0.0.1' - self.cfg.san_api_port = '8080' - - self.FAKE_RESPONSE = { - 'standard': { - 'success': {'status': 0, 'result': 'a normal message'}, - 'fail': {'status': 1, 'result': 'an error message'}} - } - - mock.patch.object(client, - 'Client', - self.create_client).start() - - mock.patch.object(disco_api, - 'DiscoApi', - self.create_client).start() - - mock.patch.object(connector.InitiatorConnector, - 'factory', - self.get_mock_connector).start() - - self.driver = driver.DiscoDriver(execute=mock_exec, - configuration=self.cfg) - self.driver.do_setup(None) - - self.attach_detach = attach_detach.AttachDetachDiscoVolume(self.cfg) - - self.ctx = context.RequestContext('fake', 'fake', auth_token=True) - self.volume = fake_volume.fake_volume_obj(self.ctx) - self.volume['volume_id'] = '1234567' - - self.requester = self.driver.client - - def create_client(self, *cmd, **kwargs): - """Mock the client's methods.""" - return FakeClient() - - def get_mock_connector(self, *cmd, **kwargs): - """Mock the os_brick connector.""" - return None - - def get_mock_attribute(self, *cmd, **kwargs): - """Mock the os_brick connector.""" - return 'DISCO' - - def get_fake_volume(self, *cmd, **kwards): - """Return a volume object for the tests.""" - return self.volume - - -class FakeClient(object): - """Fake class to mock client.""" - - def __init__(self, *args, **kwargs): - """Create a fake service attribute.""" - self.service = FakeMethod() - - -class FakeMethod(object): - """Fake class recensing some of the method of the rest client.""" - - def __init__(self, *args, **kwargs): - """Fake class to mock the client.""" - - def volumeCreate(self, *args, **kwargs): - """"Mock function to create a volume.""" - - def volumeDelete(self, *args, **kwargs): - """"Mock function to delete a volume.""" - - def snapshotCreate(self, *args, **kwargs): - """"Mock function to create a snapshot.""" - - def snapshotDetail(self, *args, **kwargs): - """"Mock function to get the snapshot detail.""" - - def snapshotDelete(self, *args, **kwargs): - """"Mock function to delete snapshot.""" - - def restoreFromSnapshot(self, *args, **kwargs): - """"Mock function to create a volume from a snapshot.""" - - def restoreDetail(self, *args, **kwargs): - """"Mock function to detail the restore operation.""" - - def volumeDetail(self, *args, **kwargs): - """Mock function to get the volume detail from its id.""" - - def volumeDetailByName(self, *args, **kwargs): - """"Mock function to get the volume detail from its name.""" - - def volumeClone(self, *args, **kwargs): - """"Mock function to clone a volume.""" - - def cloneDetail(self, *args, **kwargs): - """Mock function to get the clone detail.""" - - def volumeExtend(self, *args, **kwargs): - """Mock function to extend a volume.""" - - def systemInformationList(self, *args, **kwargs): - """Mock function to get the backend properties.""" diff --git a/cinder/tests/unit/volume/drivers/disco/test_create_cloned_volume.py b/cinder/tests/unit/volume/drivers/disco/test_create_cloned_volume.py deleted file mode 100644 index c55ebb7c0c1..00000000000 --- a/cinder/tests/unit/volume/drivers/disco/test_create_cloned_volume.py +++ /dev/null @@ -1,158 +0,0 @@ -# (c) Copyright 2015 Industrial Technology Research Institute. -# -# 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. - -"""Test cases for create cloned volume.""" - -import copy -import mock -import six -import time - - -from cinder import exception -from cinder.tests.unit import fake_volume -from cinder.tests.unit import utils -from cinder.tests.unit.volume.drivers import disco - - -class CreateCloneVolumeTestCase(disco.TestDISCODriver): - """Test cases for DISCO connector.""" - - def setUp(self): - """Initialise variables and mock functions.""" - super(CreateCloneVolumeTestCase, self).setUp() - - self.dest_volume = fake_volume.fake_volume_obj(self.ctx) - # Create mock functions for all the call done by the driver.""" - mock.patch.object(self.requester, - 'volumeClone', - self.clone_request).start() - - mock.patch.object(self.requester, - 'cloneDetail', - self.clone_detail_request).start() - - mock.patch.object(self.requester, - 'volumeDetailByName', - self.volume_detail_request).start() - - self.volume_detail_response = { - 'status': 0, - 'volumeInfoResult': - {'volumeId': 1234567} - } - - clone_success = ( - copy.deepcopy(self.FAKE_RESPONSE['standard']['success'])) - clone_pending = ( - copy.deepcopy(self.FAKE_RESPONSE['standard']['success'])) - clone_fail = ( - copy.deepcopy(self.FAKE_RESPONSE['standard']['success'])) - clone_response_fail = ( - copy.deepcopy(self.FAKE_RESPONSE['standard']['success'])) - - clone_success['result'] = ( - six.text_type(self.DETAIL_OPTIONS['success'])) - clone_pending['result'] = ( - six.text_type(self.DETAIL_OPTIONS['pending'])) - clone_fail['result'] = ( - six.text_type(self.DETAIL_OPTIONS['failure'])) - clone_response_fail['status'] = 1 - - self.FAKE_RESPONSE['clone_detail'] = { - 'success': clone_success, - 'fail': clone_fail, - 'pending': clone_pending, - 'request_fail': clone_response_fail - } - - self.response = self.FAKE_RESPONSE['standard']['success'] - self.response['result'] = '1234' - - self.response_detail = ( - self.FAKE_RESPONSE['clone_detail']['success']) - self.test_pending = False - self.test_pending_count = 0 - - def clone_request(self, *cmd, **kwargs): - """Mock function for the createVolumeFromSnapshot function.""" - return self.response - - def clone_detail_request(self, *cmd, **kwargs): - """Mock function for the restoreDetail function.""" - if self.test_pending: - if self.test_pending_count == 0: - self.test_pending_count += 1 - return self.FAKE_RESPONSE['clone_detail']['pending'] - else: - return self.FAKE_RESPONSE['clone_detail']['success'] - else: - return self.response_detail - - def volume_detail_request(self, *cmd, **kwargs): - """Mock function for the volumeDetail function.""" - return self.volume_detail_response - - def test_create_cloned_volume(self): - """Normal case.""" - expected = 1234567 - actual = self.driver.create_cloned_volume(self.dest_volume, - self.volume) - self.assertEqual(expected, actual['provider_location']) - - def test_create_clone_volume_fail(self): - """Clone volume request to DISCO fails.""" - self.response = self.FAKE_RESPONSE['standard']['fail'] - self.assertRaises(exception.VolumeBackendAPIException, - self.test_create_cloned_volume) - - def test_create_cloned_volume_fail_not_immediate(self): - """Get clone detail returns that the clone fails.""" - self.response = self.FAKE_RESPONSE['standard']['success'] - self.response_detail = ( - self.FAKE_RESPONSE['clone_detail']['fail']) - self.assertRaises(exception.VolumeBackendAPIException, - self.test_create_cloned_volume) - - def test_create_cloned_volume_fail_not_immediate_response_fail(self): - """Get clone detail request to DISCO fails.""" - self.response = self.FAKE_RESPONSE['standard']['success'] - self.response_detail = ( - self.FAKE_RESPONSE['clone_detail']['request_fail']) - self.assertRaises(exception.VolumeBackendAPIException, - self.test_create_cloned_volume) - - def test_create_cloned_volume_fail_not_immediate_request_fail(self): - """Get clone detail returns the task is pending then complete.""" - self.response = self.FAKE_RESPONSE['standard']['success'] - self.test_pending = True - self.test_create_cloned_volume() - - @mock.patch.object(time, 'time') - def test_create_cloned_volume_timeout(self, mock_time): - """Clone request timeout.""" - timeout = 3 - mock_time.side_effect = utils.generate_timeout_series(timeout) - self.driver.configuration.disco_clone_check_timeout = timeout - self.response = self.FAKE_RESPONSE['standard']['success'] - self.response_detail = ( - self.FAKE_RESPONSE['clone_detail']['pending']) - self.assertRaises(exception.VolumeBackendAPIException, - self.test_create_cloned_volume) - - def test_create_cloned_volume_volume_detail_fail(self): - """Get volume detail request to DISCO fails.""" - self.volume_detail_response['status'] = 1 - self.assertRaises(exception.VolumeBackendAPIException, - self.test_create_cloned_volume) diff --git a/cinder/tests/unit/volume/drivers/disco/test_create_snapshot.py b/cinder/tests/unit/volume/drivers/disco/test_create_snapshot.py deleted file mode 100644 index 37b3542833f..00000000000 --- a/cinder/tests/unit/volume/drivers/disco/test_create_snapshot.py +++ /dev/null @@ -1,153 +0,0 @@ -# (c) Copyright 2015 Industrial Technology Research Institute. -# -# 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. - -"""Test case for the function create snapshot.""" - - -import copy -import mock -import time - -from cinder import db -from cinder import exception -from cinder.tests.unit import fake_snapshot -from cinder.tests.unit import utils -from cinder.tests.unit.volume.drivers import disco - - -class CreateSnapshotTestCase(disco.TestDISCODriver): - """Test cases for DISCO connector.""" - - def get_fake_volume(self, ctx, id): - """Return fake volume from db calls.""" - return self.volume - - def setUp(self): - """Initialise variables and mock functions.""" - super(CreateSnapshotTestCase, self).setUp() - - self.snapshot = fake_snapshot.fake_snapshot_obj( - self.ctx, **{'volume': self.volume}) - - # Mock db call in the cinder driver - self.mock_object(db.sqlalchemy.api, 'volume_get', - self.get_fake_volume) - - mock.patch.object(self.requester, - 'snapshotCreate', - self.snapshot_request).start() - - mock.patch.object(self.requester, - 'snapshotDetail', - self.snapshot_detail_request).start() - - snapshot_detail_response = { - 'status': 0, - 'snapshotInfoResult': - {'snapshotId': 1234, - 'description': 'a description', - 'createTime': '', - 'expireTime': '', - 'isDeleted': False, - 'status': 0} - } - - snap_success = copy.deepcopy(snapshot_detail_response) - snap_pending = copy.deepcopy(snapshot_detail_response) - snap_fail = copy.deepcopy(snapshot_detail_response) - snap_response_fail = copy.deepcopy(snapshot_detail_response) - snap_success['snapshotInfoResult']['status'] = ( - self.DETAIL_OPTIONS['success']) - snap_pending['snapshotInfoResult']['status'] = ( - self.DETAIL_OPTIONS['pending']) - snap_fail['snapshotInfoResult']['status'] = ( - self.DETAIL_OPTIONS['failure']) - snap_response_fail['status'] = 1 - - self.FAKE_RESPONSE['snapshot_detail'] = { - 'success': snap_success, - 'fail': snap_fail, - 'pending': snap_pending, - 'request_fail': snap_response_fail} - - self.response = ( - self.FAKE_RESPONSE['standard']['success']) - self.response['result'] = 1234 - - self.response_detail = ( - self.FAKE_RESPONSE['snapshot_detail']['success']) - self.test_pending = False - - self.test_pending_count = 0 - - def snapshot_request(self, *cmd, **kwargs): - """Mock function for the createSnapshot call.""" - return self.response - - def snapshot_detail_request(self, *cmd, **kwargs): - """Mock function for the snapshotDetail call.""" - if self.test_pending: - if self.test_pending_count == 0: - self.test_pending_count += 1 - return self.FAKE_RESPONSE['snapshot_detail']['pending'] - else: - return self.FAKE_RESPONSE['snapshot_detail']['success'] - else: - return self.response_detail - - def test_create_snapshot(self): - """Normal test case.""" - expected = 1234 - actual = self.driver.create_snapshot(self.volume) - self.assertEqual(expected, actual['provider_location']) - - def test_create_snapshot_fail(self): - """Request to DISCO failed.""" - self.response = self.FAKE_RESPONSE['standard']['fail'] - self.assertRaises(exception.VolumeBackendAPIException, - self.test_create_snapshot) - - def test_create_snapshot_fail_not_immediate(self): - """Request to DISCO failed when monitoring the snapshot details.""" - self.response = self.FAKE_RESPONSE['standard']['success'] - self.response_detail = ( - self.FAKE_RESPONSE['snapshot_detail']['fail']) - self.assertRaises(exception.VolumeBackendAPIException, - self.test_create_snapshot) - - def test_create_snapshot_fail_not_immediate_response_fail(self): - """Request to get the snapshot details returns a failure.""" - self.response = self.FAKE_RESPONSE['standard']['success'] - self.response_detail = ( - self.FAKE_RESPONSE['snapshot_detail']['request_fail']) - self.assertRaises(exception.VolumeBackendAPIException, - self.test_create_snapshot) - - def test_create_snapshot_detail_pending(self): - """Request to get the snapshot detail return pending then success.""" - self.response = self.FAKE_RESPONSE['standard']['success'] - self.test_pending = True - self.test_create_snapshot() - - @mock.patch.object(time, 'time') - def test_create_snapshot_timeout(self, mock_time): - """Snapshot request timeout.""" - timeout = 3 - mock_time.side_effect = utils.generate_timeout_series(timeout) - self.driver.configuration.disco_snapshot_check_timeout = timeout - self.response = self.FAKE_RESPONSE['standard']['success'] - self.response_detail = ( - self.FAKE_RESPONSE['snapshot_detail']['pending']) - self.assertRaises(exception.VolumeBackendAPIException, - self.test_create_snapshot) diff --git a/cinder/tests/unit/volume/drivers/disco/test_create_volume.py b/cinder/tests/unit/volume/drivers/disco/test_create_volume.py deleted file mode 100644 index d4006d28998..00000000000 --- a/cinder/tests/unit/volume/drivers/disco/test_create_volume.py +++ /dev/null @@ -1,53 +0,0 @@ -# (c) Copyright 2015 Industrial Technology Research Institute. -# -# 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. - -"""Test case for the create volume function.""" - -import mock - -from cinder import exception -from cinder.tests.unit.volume.drivers import disco - - -class CreateVolumeTestCase(disco.TestDISCODriver): - """Test cases for DISCO connector.""" - - def setUp(self): - """Prepare variables and mock functions.""" - super(CreateVolumeTestCase, self).setUp() - - # Mock the method volumeCreate. - mock.patch.object(self.requester, - 'volumeCreate', - self.perform_disco_request).start() - - self.response = self.FAKE_RESPONSE['standard']['success'] - - def perform_disco_request(self, *cmd, **kwargs): - """Mock function for the suds client.""" - return self.response - - def test_create_volume(self): - """Normal case.""" - expected = '1234567' - self.response['result'] = expected - ret = self.driver.create_volume(self.volume) - actual = ret['provider_location'] - self.assertEqual(expected, actual) - - def test_create_volume_fail(self): - """Request to DISCO failed.""" - self.response = self.FAKE_RESPONSE['standard']['fail'] - self.assertRaises(exception.VolumeBackendAPIException, - self.test_create_volume) diff --git a/cinder/tests/unit/volume/drivers/disco/test_create_volume_from_snapshot.py b/cinder/tests/unit/volume/drivers/disco/test_create_volume_from_snapshot.py deleted file mode 100644 index a0c2ac00fa3..00000000000 --- a/cinder/tests/unit/volume/drivers/disco/test_create_volume_from_snapshot.py +++ /dev/null @@ -1,166 +0,0 @@ -# (c) Copyright 2015 Industrial Technology Research Institute. -# -# 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. - -"""Test case for create volume from snapshot.""" - -import copy -import mock -import time - -from cinder import exception -from cinder.tests.unit import fake_snapshot -from cinder.tests.unit import utils -from cinder.tests.unit.volume.drivers import disco - - -class CreateVolumeFromSnapshotTestCase(disco.TestDISCODriver): - """Test cases for the create volume from snapshot of DISCO connector.""" - - def setUp(self): - """Initialise variables and mock functions.""" - super(CreateVolumeFromSnapshotTestCase, self).setUp() - - self.snapshot = fake_snapshot.fake_snapshot_obj( - self.ctx, **{'volume': self.volume}) - - # Mock restoreFromSnapshot, restoreDetail - # and volume detail since they are in the function path - mock.patch.object(self.requester, - 'restoreFromSnapshot', - self.restore_request).start() - - mock.patch.object(self.requester, - 'restoreDetail', - self.restore_detail_request).start() - - mock.patch.object(self.requester, - 'volumeDetailByName', - self.volume_detail_request).start() - - restore_detail_response = { - 'status': 0, - 'restoreInfoResult': - {'restoreId': 1234, - 'startTime': '', - 'statusPercent': '', - 'volumeName': 'aVolumeName', - 'snapshotId': 1234, - 'status': 0} - } - - self.volume_detail_response = { - 'status': 0, - 'volumeInfoResult': - {'volumeId': 1234567} - } - - rest_success = copy.deepcopy(restore_detail_response) - rest_pending = copy.deepcopy(restore_detail_response) - rest_fail = copy.deepcopy(restore_detail_response) - rest_response_fail = copy.deepcopy(restore_detail_response) - rest_success['restoreInfoResult']['status'] = ( - self.DETAIL_OPTIONS['success']) - rest_pending['restoreInfoResult']['status'] = ( - self.DETAIL_OPTIONS['pending']) - rest_fail['restoreInfoResult']['status'] = ( - self.DETAIL_OPTIONS['failure']) - rest_response_fail['status'] = 1 - - self.FAKE_RESPONSE['restore_detail'] = { - 'success': rest_success, - 'fail': rest_fail, - 'pending': rest_pending, - 'request_fail': rest_response_fail - } - - self.response = self.FAKE_RESPONSE['standard']['success'] - self.response['result'] = '1234' - - self.response_detail = ( - self.FAKE_RESPONSE['restore_detail']['success']) - self.test_pending = False - - self.test_pending_count = 0 - - def restore_request(self, *cmd, **kwargs): - """Mock function for the createVolumeFromSnapshot function.""" - return self.response - - def restore_detail_request(self, *cmd, **kwargs): - """Mock function for the restoreDetail function.""" - if self.test_pending: - if self.test_pending_count == 0: - self.test_pending_count += 1 - return self.FAKE_RESPONSE['restore_detail']['pending'] - else: - return self.FAKE_RESPONSE['restore_detail']['success'] - else: - return self.response_detail - - def volume_detail_request(self, *cmd, **kwargs): - """Mock function for the volumeDetail function.""" - return self.volume_detail_response - - def test_create_volume_from_snapshot(self): - """Normal case.""" - expected = 1234567 - actual = self.driver.create_volume_from_snapshot(self.volume, - self.snapshot) - self.assertEqual(expected, actual['provider_location']) - - def test_create_volume_from_snapshot_fail(self): - """Create volume from snapshot request fails.""" - self.response = self.FAKE_RESPONSE['standard']['fail'] - self.assertRaises(exception.VolumeBackendAPIException, - self.test_create_volume_from_snapshot) - - def test_create_volume_from_snapshot_fail_not_immediate(self): - """Get restore details request fails.""" - self.response = self.FAKE_RESPONSE['standard']['success'] - self.response_detail = ( - self.FAKE_RESPONSE['restore_detail']['fail']) - self.assertRaises(exception.VolumeBackendAPIException, - self.test_create_volume_from_snapshot) - - def test_create_volume_from_snapshot_fail_detail_response_fail(self): - """Get restore details reports that restore operation fails.""" - self.response = self.FAKE_RESPONSE['standard']['success'] - self.response_detail = ( - self.FAKE_RESPONSE['restore_detail']['request_fail']) - self.assertRaises(exception.VolumeBackendAPIException, - self.test_create_volume_from_snapshot) - - def test_create_volume_from_snapshot_fail_not_immediate_resp_fail(self): - """Get restore details reports that the task is pending, then done.""" - self.response = self.FAKE_RESPONSE['standard']['success'] - self.test_pending = True - self.test_create_volume_from_snapshot() - - @mock.patch.object(time, 'time') - def test_create_volume_from_snapshot_timeout(self, mock_time): - """Create volume from snapshot task timeout.""" - timeout = 3 - mock_time.side_effect = utils.generate_timeout_series(timeout) - self.driver.configuration.disco_restore_check_timeout = timeout - self.response = self.FAKE_RESPONSE['standard']['success'] - self.response_detail = ( - self.FAKE_RESPONSE['restore_detail']['pending']) - self.assertRaises(exception.VolumeBackendAPIException, - self.test_create_volume_from_snapshot) - - def test_create_volume_from_snapshot_volume_detail_fail(self): - """Cannot get the newly created volume information.""" - self.volume_detail_response['status'] = 1 - self.assertRaises(exception.VolumeBackendAPIException, - self.test_create_volume_from_snapshot) diff --git a/cinder/tests/unit/volume/drivers/disco/test_delete_snapshot.py b/cinder/tests/unit/volume/drivers/disco/test_delete_snapshot.py deleted file mode 100644 index e102957c6c2..00000000000 --- a/cinder/tests/unit/volume/drivers/disco/test_delete_snapshot.py +++ /dev/null @@ -1,51 +0,0 @@ -# (c) Copyright 2015 Industrial Technology Research Institute. -# -# 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. - -"""Test case for the delete snapshot function.""" -import mock - -from cinder import exception -from cinder.tests.unit import fake_snapshot -from cinder.tests.unit.volume.drivers import disco - - -class DeleteSnapshotTestCase(disco.TestDISCODriver): - """Test cases to delete DISCO volumes.""" - - def setUp(self): - """Initialise variables and mock functions.""" - super(DeleteSnapshotTestCase, self).setUp() - - # Mock snapshotDelete function. - mock.patch.object(self.requester, - 'snapshotDelete', - self.perform_disco_request).start() - - self.response = self.FAKE_RESPONSE['standard']['success'] - self.snapshot = fake_snapshot.fake_snapshot_obj( - self.ctx, **{'volume': self.volume}) - - def perform_disco_request(self, *cmd, **kwargs): - """Mock function to delete a snapshot.""" - return self.response - - def test_delete_snapshot(self): - """Delete a snapshot.""" - self.driver.delete_snapshot(self.snapshot) - - def test_delete_snapshot_fail(self): - """Make the API returns an error while deleting.""" - self.response = self.FAKE_RESPONSE['standard']['fail'] - self.assertRaises(exception.VolumeBackendAPIException, - self.test_delete_snapshot) diff --git a/cinder/tests/unit/volume/drivers/disco/test_delete_volume.py b/cinder/tests/unit/volume/drivers/disco/test_delete_volume.py deleted file mode 100644 index 8bbf1680210..00000000000 --- a/cinder/tests/unit/volume/drivers/disco/test_delete_volume.py +++ /dev/null @@ -1,49 +0,0 @@ -# (c) Copyright 2015 Industrial Technology Research Institute. -# -# 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. - -"""Test case for the delete volume function.""" - -import mock - -from cinder import exception -from cinder.tests.unit.volume.drivers import disco - - -class DeleteVolumeTestCase(disco.TestDISCODriver): - """Test cases to delete DISCO volumes.""" - - def setUp(self): - """Initialise variables and mock functions.""" - super(DeleteVolumeTestCase, self).setUp() - - # Mock volumeDelete function. - mock.patch.object(self.requester, - 'volumeDelete', - self.perform_disco_request).start() - - self.response = self.FAKE_RESPONSE['standard']['success'] - - def perform_disco_request(self, *cmd, **kwargs): - """Mock function to delete a volume.""" - return self.response - - def test_delete_volume(self): - """Delete a volume.""" - self.driver.delete_volume(self.volume) - - def test_delete_volume_fail(self): - """Make the API returns an error while deleting.""" - self.response = self.FAKE_RESPONSE['standard']['fail'] - self.assertRaises(exception.VolumeBackendAPIException, - self.test_delete_volume) diff --git a/cinder/tests/unit/volume/drivers/disco/test_extend_volume.py b/cinder/tests/unit/volume/drivers/disco/test_extend_volume.py deleted file mode 100644 index c1924ac3d15..00000000000 --- a/cinder/tests/unit/volume/drivers/disco/test_extend_volume.py +++ /dev/null @@ -1,50 +0,0 @@ -# (c) Copyright 2015 Industrial Technology Research Institute. -# -# 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. - -"""Test cases for the extend volume feature.""" - -import mock - -from cinder import exception -from cinder.tests.unit.volume.drivers import disco - - -class VolumeExtendTestCase(disco.TestDISCODriver): - """Test cases for DISCO connector.""" - - def setUp(self): - """Initialise variables and mock functions.""" - super(VolumeExtendTestCase, self).setUp() - - # Mock function to extend a volume. - mock.patch.object(self.requester, - 'volumeExtend', - self.perform_disco_request).start() - - self.response = self.FAKE_RESPONSE['standard']['success'] - self.new_size = 5 - - def perform_disco_request(self, *cmd, **kwargs): - """Mock volumExtend function from suds client.""" - return self.response - - def test_extend_volume(self): - """Extend a volume, normal case.""" - self.driver.extend_volume(self.volume, self.new_size) - - def test_extend_volume_fail(self): - """Request to DISCO failed.""" - self.response = self.FAKE_RESPONSE['standard']['fail'] - self.assertRaises(exception.VolumeBackendAPIException, - self.test_extend_volume) diff --git a/cinder/tests/unit/volume/drivers/disco/test_manage_existing.py b/cinder/tests/unit/volume/drivers/disco/test_manage_existing.py deleted file mode 100644 index c0366978011..00000000000 --- a/cinder/tests/unit/volume/drivers/disco/test_manage_existing.py +++ /dev/null @@ -1,133 +0,0 @@ -# (c) Copyright 2016 Industrial Technology Research Institute. -# -# 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. - -"""Test case for the function manage_existing.""" - -import mock - -from cinder import exception -from cinder.tests.unit.volume.drivers import disco - - -class ManageExistingTestCase(disco.TestDISCODriver): - """Test cases for Disco connector.""" - - def setUp(self): - """Initialize variables and mock functions.""" - super(ManageExistingTestCase, self).setUp() - - # Mock function to extract volume information by its ID - mock.patch.object(self.requester, - 'volumeDetail', - self.perform_disco_request).start() - - # Mock function to extract volume information by its Name - mock.patch.object(self.requester, - 'volumeDetailByName', - self.perform_disco_request).start() - - self.response = {'volumeInfoResult': { - 'volumeName': 'abcdefg', - 'volumeId': 1234567, - 'volSizeMb': 2 - }, - 'status': 0 - } - - self.existing_ref_no_identification = {} - self.existing_ref_with_id = {'source-id': 1234567} - self.existing_ref_with_name = {'source-name': 'abcdefg'} - self.existing_ref_no_identification = self.existing_ref_with_id - - def perform_disco_request(self, *args, **kwargs): - """Mock volumeDetail/volumeDetailByName function from rest client.""" - return self.response - - def call_manage_existing(self): - """Manage an existing volume.""" - self.driver.manage_existing( - self.volume, - self.existing_ref_no_identification) - - def test_manage_existing_no_identification(self): - """Manage an existing volume, no id/name.""" - self.existing_ref_no_identification = {} - self.assertRaises(exception.VolumeBackendAPIException, - self.call_manage_existing) - - def test_manage_existing_case_id(self): - """Manage an existing volume, by its id.""" - expected = {'display_name': 'abcdefg'} - ret = self.driver.manage_existing(self.volume, - self.existing_ref_with_id) - actual = {'display_name': ret['display_name']} - self.assertEqual(expected, actual) - - def test_manage_existing_case_name(self): - """Manage an existing volume, by its name.""" - expected = {'provider_location': 1234567} - ret = self.driver.manage_existing(self.volume, - self.existing_ref_with_name) - actual = {'provider_location': ret['provider_location']} - self.assertEqual(expected, actual) - - def test_manage_existing_get_size(self): - """Get size of an existing volume.""" - self.driver.manage_existing_get_size( - self.volume, - self.existing_ref_no_identification) - - def test_manage_existing_get_size_no_identification(self): - """Error while getting size of an existing volume, no id/name.""" - self.existing_ref_no_identification = {} - self.assertRaises(exception.VolumeBackendAPIException, - self.test_manage_existing_get_size) - - def test_manage_existing_get_size_case_id(self): - """Get size of an existing volume, by its id.""" - expected = 2 - ret = self.driver.manage_existing_get_size(self.volume, - self.existing_ref_with_id) - self.assertEqual(expected, ret) - - def test_manage_existing_get_size_case_name(self): - """Get size of an existing volume, by its name.""" - expected = 2 - ret = self.driver.manage_existing_get_size(self.volume, - self.existing_ref_with_name) - self.assertEqual(expected, ret) - - def test_manage_existing_case_id_fail(self): - """Request to DISCO failed.""" - self.response['status'] = 1 - self.assertRaises(exception.VolumeBackendAPIException, - self.test_manage_existing_case_id) - - def test_manage_existing_case_name_fail(self): - """Request to DISCO failed.""" - self.response['status'] = 1 - self.assertRaises(exception.VolumeBackendAPIException, - self.test_manage_existing_case_name) - - def test_manage_existing_get_size_case_id_fail(self): - """Request to DISCO failed.""" - self.response['status'] = 1 - self.assertRaises(exception.VolumeBackendAPIException, - self.test_manage_existing_get_size_case_id) - - def test_manage_existing_get_size_case_name_fail(self): - """Request to DISCO failed.""" - self.response['status'] = 1 - self.assertRaises(exception.VolumeBackendAPIException, - self.test_manage_existing_get_size_case_name) diff --git a/cinder/volume/drivers/disco/__init__.py b/cinder/volume/drivers/disco/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/cinder/volume/drivers/disco/disco.py b/cinder/volume/drivers/disco/disco.py deleted file mode 100644 index 707691826fc..00000000000 --- a/cinder/volume/drivers/disco/disco.py +++ /dev/null @@ -1,650 +0,0 @@ -# copyright (c) 2016 Industrial Technology Research Institute. -# 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. - -"""DISCO Block device Driver.""" - -import os -import time - -from oslo_config import cfg -from oslo_log import log as logging -from oslo_service import loopingcall -from oslo_utils import units -import six -from suds import client - -from cinder import context -from cinder.db.sqlalchemy import api -from cinder import exception -from cinder.i18n import _ -from cinder.image import image_utils -from cinder import interface -from cinder.volume import configuration -from cinder.volume import driver -from cinder.volume.drivers.disco import disco_api -from cinder.volume.drivers.disco import disco_attach_detach -from cinder.volume.drivers.san import san - - -LOG = logging.getLogger(__name__) - -disco_opts = [ - cfg.IPOpt('disco_client', - default='127.0.0.1', - help='The IP of DMS client socket server', - deprecated_group='DEFAULT'), - cfg.PortOpt('disco_client_port', - default=9898, - help='The port to connect DMS client socket server', - deprecated_group='DEFAULT'), - cfg.StrOpt('disco_wsdl_path', - default='/etc/cinder/DISCOService.wsdl', - deprecated_for_removal=True, - help='Path to the wsdl file ' - 'to communicate with DISCO request manager'), - cfg.IPOpt('disco_rest_ip', - help='The IP address of the REST server', - deprecated_for_removal=True, - deprecated_reason='Using san_ip later', - deprecated_name='rest_ip', deprecated_group='DEFAULT'), - cfg.StrOpt('disco_choice_client', - help='Use soap client or rest client for communicating ' - 'with DISCO. Possible values are "soap" or ' - '"rest".', choices=['soap', 'rest'], - deprecated_name='choice_client', deprecated_group='DEFAULT'), - cfg.PortOpt('disco_src_api_port', - default=8080, - deprecated_for_removal=True, - deprecated_reason='Using san_api_port later', - help='The port of DISCO source API', - deprecated_group='DEFAULT'), - cfg.StrOpt('disco_volume_name_prefix', - default='openstack-', - help='Prefix before volume name to differentiate ' - 'DISCO volume created through openstack ' - 'and the other ones', - deprecated_name='volume_name_prefix'), - cfg.IntOpt('disco_snapshot_check_timeout', - default=3600, - help='How long we check whether a snapshot ' - 'is finished before we give up', - deprecated_name='snapshot_check_timeout'), - cfg.IntOpt('disco_restore_check_timeout', - default=3600, - help='How long we check whether a restore ' - 'is finished before we give up', - deprecated_name='restore_check_timeout'), - cfg.IntOpt('disco_clone_check_timeout', - default=3600, - help='How long we check whether a clone ' - 'is finished before we give up', - deprecated_name='clone_check_timeout'), - cfg.IntOpt('disco_retry_interval', - default=1, - help='How long we wait before retrying to ' - 'get an item detail', - deprecated_name='retry_interval') -] - -DISCO_CODE_MAPPING = { - 'request.success': 1, - 'request.ongoing': 2, - 'request.failure': 3, -} - -CONF = cfg.CONF -CONF.register_opts(disco_opts, group=configuration.SHARED_CONF_GROUP) - - -# Driver to communicate with DISCO storage solution -@interface.volumedriver -class DiscoDriver(driver.VolumeDriver): - """Execute commands related to DISCO Volumes. - - .. code-block:: text - - Version history: - 1.0 - disco volume driver using SOAP - 1.1 - disco volume driver using REST and only compatible - with version greater than disco-1.6.4 - - """ - - VERSION = "1.1" - CI_WIKI_NAME = "ITRI_DISCO_CI" - - # TODO(jsbryant) Remove driver in Stein if CI is not fixed - SUPPORTED = False - - def __init__(self, *args, **kwargs): - """Init Disco driver : get configuration, create client.""" - super(DiscoDriver, self).__init__(*args, **kwargs) - self.configuration.append_config_values(disco_opts) - self.configuration.append_config_values(san.san_opts) - self.ctxt = context.get_admin_context() - self.attach_detach_volume = ( - disco_attach_detach.AttachDetachDiscoVolume(self.configuration)) - - def do_setup(self, context): - """Create client for DISCO request manager.""" - LOG.debug("Enter in DiscoDriver do_setup.") - if (self.configuration.disco_choice_client.lower() == "rest" and - self.configuration.san_ip): - self.client = disco_api.DiscoApi( - self.configuration.san_ip, - self.configuration.san_api_port) - elif (self.configuration.disco_choice_client.lower() == "rest" and - self.configuration.disco_rest_ip): - self.client = disco_api.DiscoApi( - self.configuration.disco_rest_ip, - self.configuration.disco_src_api_port) - else: - path = ''.join(['file:', self.configuration.disco_wsdl_path]) - init_client = client.Client(path, cache=None) - self.client = init_client.service - - def check_for_setup_error(self): - """Make sure we have the pre-requisites.""" - if self.configuration.disco_choice_client.lower() == "soap": - path = self.configuration.disco_wsdl_path - if not os.path.exists(path): - msg = _("Could not find DISCO wsdl file.") - raise exception.VolumeBackendAPIException(data=msg) - else: - if not (self.configuration.disco_rest_ip or - self.configuration.san_ip): - msg = _("Could not find the IP address of the REST server.") - raise exception.VolumeBackendAPIException(data=msg) - - def create_volume(self, volume): - """Create a disco volume.""" - name = self.configuration.disco_volume_name_prefix, volume["id"] - vol_name = ''.join(name) - vol_size = volume['size'] * units.Ki - LOG.debug("Create volume : [name] %(vname)s - [size] %(vsize)s.", - {'vname': vol_name, 'vsize': six.text_type(vol_size)}) - reply = self.client.volumeCreate(vol_name, vol_size) - status = reply['status'] - result = reply['result'] - LOG.debug("Create volume : [status] %(stat)s - [result] %(res)s.", - {'stat': six.text_type(status), 'res': result}) - - if status: - msg = (_("Error while creating volume " - "[status] %(stat)s - [result] %(res)s.") % - {'stat': six.text_type(status), 'res': result}) - LOG.error(msg) - raise exception.VolumeBackendAPIException(data=msg) - LOG.debug("Volume %s created.", volume["name"]) - return {'provider_location': result} - - def delete_volume(self, volume): - """Delete a logical volume.""" - disco_vol_id = volume['provider_location'] - LOG.debug("Delete disco volume : %s.", disco_vol_id) - reply = self.client.volumeDelete(disco_vol_id) - status = reply['status'] - result = reply['result'] - - LOG.debug("Delete volume [status] %(stat)s - [result] %(res)s.", - {'stat': six.text_type(status), 'res': result}) - - if status: - msg = (_("Error while deleting volume " - "[status] %(stat)s - [result] %(res)s.") % - {'stat': six.text_type(status), 'res': result}) - LOG.error(msg) - raise exception.VolumeBackendAPIException(data=msg) - - LOG.debug("Volume %s deleted.", volume['name']) - - def create_snapshot(self, snapshot): - """Create a disco snapshot.""" - volume = api.volume_get(self.ctxt, snapshot['volume_id']) - description = snapshot['display_description'] - vol_id = volume['provider_location'] - LOG.debug("Create snapshot of volume : %(id)s, " - "description : %(desc)s.", - {'id': vol_id, 'desc': description}) - - # Trigger an asynchronous local snapshot - reply = self.client.snapshotCreate(vol_id, - -1, -1, - description) - status = reply['status'] - result = reply['result'] - LOG.debug("Create snapshot : [status] %(stat)s - [result] %(res)s.", - {'stat': six.text_type(status), 'res': result}) - - if status: - msg = (_("Error while creating snapshot " - "[status] %(stat)s - [result] %(res)s.") % - {'stat': six.text_type(status), 'res': result}) - LOG.error(msg) - raise exception.VolumeBackendAPIException(data=msg) - - # Monitor the status until it becomes either success or fail - params = {'snapshot_id': int(result)} - start_time = int(time.time()) - snapshot_request = DISCOCheck(self.client, - params, - start_time, - "snapshot_detail", - self.configuration) - timeout = self.configuration.disco_snapshot_check_timeout - snapshot_request._monitor_request(timeout) - - snapshot['provider_location'] = result - LOG.debug("snapshot taken successfully on volume : %(volume)s.", - {'volume': volume['name']}) - return {'provider_location': result} - - def delete_snapshot(self, snapshot): - """Delete a disco snapshot.""" - LOG.debug("Enter in delete a disco snapshot.") - - snap_id = snapshot['provider_location'] - LOG.debug("[start] Delete snapshot : %s.", snap_id) - reply = self.client.snapshotDelete(snap_id) - status = reply['status'] - result = reply['result'] - LOG.debug("[End] Delete snapshot : " - "[status] %(stat)s - [result] %(res)s.", - {'stat': six.text_type(status), 'res': result}) - - if status: - msg = (_("Error while deleting snapshot " - "[status] %(stat)s - [result] %(res)s") % - {'stat': six.text_type(status), 'res': result}) - LOG.error(msg) - raise exception.VolumeBackendAPIException(data=msg) - - def create_volume_from_snapshot(self, volume, snapshot): - """Create a volume from a snapshot.""" - name = self.configuration.disco_volume_name_prefix, volume['id'] - snap_id = snapshot['provider_location'] - vol_name = ''.join(name) - # Trigger an asynchronous restore operation - LOG.debug("[start] Create volume from snapshot : " - "%(snap_id)s - name : %(vol_name)s.", - {'snap_id': snap_id, 'vol_name': vol_name}) - reply = self.client.restoreFromSnapshot(snap_id, vol_name, -1, None, - -1) - status = reply['status'] - result = reply['result'] - LOG.debug("Restore volume from snapshot " - "[status] %(stat)s - [result] %(res)s.", - {'stat': six.text_type(status), 'res': result}) - - if status: - msg = (_("Error[%(stat)s - %(res)s] while restoring snapshot " - "[%(snap_id)s] into volume [%(vol)s].") % - {'stat': six.text_type(status), 'res': result, - 'snap_id': snap_id, 'vol': vol_name}) - LOG.error(msg) - raise exception.VolumeBackendAPIException(data=msg) - - # Monitor the status until it becomes - # either success, fail or timeout - params = {'restore_id': int(result)} - start_time = int(time.time()) - restore_request = DISCOCheck(self.client, - params, - start_time, - "restore_detail", - self.configuration) - timeout = self.configuration.disco_restore_check_timeout - restore_request._monitor_request(timeout) - reply = self.client.volumeDetailByName(vol_name) - status = reply['status'] - new_vol_id = reply['volumeInfoResult']['volumeId'] - - if status: - msg = (_("Error[status] %(stat)s - [result] %(res)s] " - "while getting volume id.") % - {'stat': six.text_type(status), 'res': result}) - LOG.error(msg) - raise exception.VolumeBackendAPIException(data=msg) - LOG.debug("Restore done [status] %(stat)s - " - "[volume id] %(vol_id)s.", - {'stat': status, 'vol_id': six.text_type(new_vol_id)}) - return {'provider_location': new_vol_id} - - def create_cloned_volume(self, volume, src_vref): - """Create a clone of the specified volume.""" - LOG.debug("Creating clone of volume: %s.", src_vref['id']) - name = self.configuration.disco_volume_name_prefix, volume['id'] - vol_name = ''.join(name) - vol_size = volume['size'] * units.Ki - src_vol_id = src_vref['provider_location'] - LOG.debug("Clone volume : " - "[name] %(name)s - [source] %(source)s - [size] %(size)s.", - {'name': vol_name, - 'source': src_vol_id, - 'size': six.text_type(vol_size)}) - reply = self.client.volumeClone(src_vol_id, vol_name) - status = reply['status'] - result = reply['result'] - LOG.debug("Clone volume : [status] %(stat)s - [result] %(res)s.", - {'stat': six.text_type(status), 'res': result}) - - if status: - msg = (_("Error while creating volume " - "[status] %(stat)s - [result] %(res)s.") % - {'stat': six.text_type(status), 'res': result}) - LOG.error(msg) - raise exception.VolumeBackendAPIException(data=msg) - - # Monitor the status until it becomes - # either success, fail or timeout - params = {'clone_id': int(result), - 'vol_name': vol_name} - start_time = int(time.time()) - clone_request = DISCOCheck(self.client, - params, - start_time, - "clone_detail", - self.configuration) - clone_request._monitor_request( - self.configuration.disco_clone_check_timeout) - reply = self.client.volumeDetailByName(vol_name) - status = reply['status'] - new_vol_id = reply['volumeInfoResult']['volumeId'] - - if status: - msg = (_("Error[%(stat)s - %(res)s] " - "while getting volume id."), - {'stat': six.text_type(status), 'res': result}) - LOG.error(msg) - raise exception.VolumeBackendAPIException(data=msg) - - LOG.debug("clone done : " - "[status] %(stat)s - [volume id] %(vol_id)s.", - {'stat': status, 'vol_id': six.text_type(new_vol_id)}) - return {'provider_location': new_vol_id} - - def copy_image_to_volume(self, context, volume, image_service, image_id): - """Fetch the image from image_service and write it to the volume.""" - LOG.debug("Enter in copy image to volume for disco.") - - try: - attach_detach_volume = ( - disco_attach_detach.AttachDetachDiscoVolume( - self.configuration)) - device_info = attach_detach_volume._attach_volume(volume) - image_utils.fetch_to_raw(context, - image_service, - image_id, - device_info['path'], - self.configuration.volume_dd_blocksize, - size=volume['size']) - finally: - attach_detach_volume._detach_volume(volume) - - def copy_volume_to_image(self, context, volume, image_service, image_meta): - """Copy a volume to a new image.""" - LOG.debug("Enter in copy image to volume for disco.") - try: - attach_detach_volume = ( - disco_attach_detach.AttachDetachDiscoVolume( - self.configuration)) - device_info = attach_detach_volume._attach_volume(volume) - image_utils.upload_volume(context, - image_service, - image_meta, - device_info['path']) - finally: - attach_detach_volume._detach_volume(volume) - - def extend_volume(self, volume, new_size): - """Extend an existing volume's size.""" - vol_id = volume['provider_location'] - LOG.debug("Extends volume : %(id)s, new size : %(size)s.", - {'id': vol_id, 'size': new_size}) - new_size_mb = new_size * units.Ki - reply = self.client.volumeExtend(vol_id, new_size_mb) - status = reply['status'] - result = reply['result'] - if status: - msg = (_("Error while extending volume " - "[status] %(stat)s - [result] %(res)s."), - {'stat': six.text_type(status), 'res': result}) - LOG.error(msg) - raise exception.VolumeBackendAPIException(data=msg) - LOG.debug("Volume extended : [id] %(vid)s - " - "[status] %(stat)s - [result] %(res)s.", - {'vid': vol_id, - 'stat': six.text_type(status), - 'res': result}) - - def initialize_connection(self, volume, connector): - """Function called before attaching a volume.""" - LOG.debug("Enter in initialize connection with disco, " - "connector is %s.", connector) - cp = self.attach_detach_volume._get_connection_properties(volume) - data = { - 'driver_volume_type': 'disco', - 'data': cp - } - LOG.debug("Initialize connection [data]: %s.", data) - return data - - def terminate_connection(self, volume, connector, **kwargs): - """Function called after attaching a volume.""" - LOG.debug("Enter in terminate connection with disco.") - - def _update_volume_stats(self): - LOG.debug("Enter in update volume stats.") - stats = {} - backend_name = self.configuration.safe_get('volume_backend_name') - stats['volume_backend_name'] = backend_name or 'disco' - stats['storage_protocol'] = 'disco' - stats['driver_version'] = self.VERSION - stats['reserved_percentage'] = 0 - stats['vendor_name'] = 'ITRI' - stats['QoS_support'] = False - - try: - reply = self.client.systemInformationList() - status = reply['status'] - - if status: - msg = (_("Error while getting " - "disco information [%s].") % - six.text_type(status)) - LOG.error(msg) - raise exception.VolumeBackendAPIException(data=msg) - - info_list = reply['propertyListResult']['PropertyInfoList'] - for info in info_list: - if info['name'] == 'freeCapacityGB': - stats['free_capacity_gb'] = float(info['value']) - elif info['name'] == 'totalCapacityGB': - stats['total_capacity_gb'] = float(info['value']) - except Exception: - stats['total_capacity_gb'] = 'unknown' - stats['free_capacity_gb'] = 'unknown' - - self._stats = stats - - def get_volume_stats(self, refresh=False): - """Get backend information.""" - if refresh: - self._update_volume_stats() - return self._stats - - def local_path(self, volume): - """Return the path to the DISCO volume.""" - return "/dev/dms%s" % volume['name'] - - def manage_existing(self, volume, existing_ref): - """Manage an existing volume.""" - if 'source-name' not in existing_ref and'source-id'not in existing_ref: - msg = _("No source-id/source-name in existing_ref") - LOG.error(msg) - raise exception.VolumeBackendAPIException(data=msg) - elif 'source-name' not in existing_ref: - src_vol = self.client.volumeDetail( - existing_ref['source-id']) - if src_vol['status']: - vol_id = existing_ref['source-id'] - msg = (_("Error while getting volume details, " - "[status] %(stat)s - [volume id] %(vol_id)s") % - {'stat': six.text_type(src_vol['status']), - 'vol_id': vol_id}) - LOG.error(msg) - raise exception.VolumeBackendAPIException(data=msg) - return {'display_name': src_vol['volumeInfoResult']['volumeName'], - 'provider_location': existing_ref['source-id']} - else: - src_vol = self.client.volumeDetailByName( - existing_ref['source-name']) - if src_vol['status']: - vol_name = existing_ref['source-name'] - msg = (_("Error while getting volume details with the name " - "%(name)s: [status] %(stat)s") % {'name': vol_name, - 'stat': src_vol['status']}) - LOG.error(msg) - raise exception.VolumeBackendAPIException(data=msg) - return { - 'provider_location': src_vol['volumeInfoResult']['volumeId']} - - def unmanage(self, volume): - """Unmanage an existing volume.""" - LOG.debug("unmanage is called", resource=volume) - - def manage_existing_get_size(self, volume, existing_ref): - """Return size of an existing volume.""" - if 'source-name' not in existing_ref and'source-id'not in existing_ref: - msg = _("No source-id/source-name in existing_ref") - LOG.error(msg) - raise exception.VolumeBackendAPIException(data=msg) - elif 'source-name' not in existing_ref: - src_vol = self.client.volumeDetail( - existing_ref['source-id']) - if src_vol['status']: - vol_id = existing_ref['source-id'] - msg = (_("Error while getting volume details, " - "[status] %(stat)s - [volume id] %(vol_id)s") % - {'stat': six.text_type(src_vol['status']), - 'vol_id': vol_id}) - LOG.error(msg) - raise exception.VolumeBackendAPIException(data=msg) - return src_vol['volumeInfoResult']['volSizeMb'] - else: - src_vol = self.client.volumeDetailByName( - existing_ref['source-name']) - if src_vol['status']: - vol_name = existing_ref['source-name'] - msg = (_("Error while getting volume details with the name " - "%(name)s: [status] %(stat)s") % {'name': vol_name, - 'stat': src_vol['status']}) - LOG.error(msg) - raise exception.VolumeBackendAPIException(data=msg) - return src_vol['volumeInfoResult']['volSizeMb'] - - def ensure_export(self, context, volume): - """Ensure an export.""" - pass - - def create_export(self, context, volume, connector): - """Export the volume.""" - pass - - def remove_export(self, context, volume): - """Remove an export for a logical volume.""" - pass - - -class DISCOCheck(object): - """Used to monitor DISCO operations.""" - - def __init__(self, client, param, start_time, function, configuration): - """Init some variables for checking some requests done in DISCO.""" - self.start_time = start_time - self.function = function - self.client = client - self.param = param - self.configuration = configuration - - def is_timeout(self, start_time, timeout): - """Check whether we reach the timeout.""" - current_time = int(time.time()) - return current_time - start_time > timeout - - def _retry_get_detail(self, start_time, timeout, operation, params): - """Keep trying to query an item detail unless we reach the timeout.""" - reply = self._call_api(operation, params) - status = reply['status'] - msg = (_("Error while getting %(op)s details, " - "returned code: %(status)s.") % - {'op': operation, 'status': six.text_type(status)}) - if status: - LOG.error(msg) - raise exception.VolumeBackendAPIException(data=msg) - - item_status = self._get_item_status(operation, reply) - if item_status == DISCO_CODE_MAPPING['request.failure']: - LOG.error(msg) - raise exception.VolumeBackendAPIException(data=msg) - - elif item_status == DISCO_CODE_MAPPING['request.success']: - raise loopingcall.LoopingCallDone(retvalue=reply) - elif self.is_timeout(start_time, timeout): - msg = (_("Timeout while calling %s ") % operation) - LOG.error(msg) - raise exception.VolumeBackendAPIException(data=msg) - - def _call_api(self, operation, params): - """Make the call to the SOAP api.""" - if operation == 'snapshot_detail': - return self.client.snapshotDetail(params['snapshot_id']) - if operation == 'restore_detail': - return self.client.restoreDetail(params['restore_id']) - if operation == 'clone_detail': - return self.client.cloneDetail(params['clone_id'], - params['vol_name']) - else: - msg = (_("Unknown operation %s."), operation) - LOG.error(msg) - raise exception.VolumeBackendAPIException(data=msg) - - def _get_item_status(self, operation, reply): - """Make the call to the SOAP api.""" - if reply is None: - msg = (_("Call returned a None object")) - LOG.error(msg) - raise exception.VolumeBackendAPIException(data=msg) - elif operation == 'snapshot_detail': - return reply['snapshotInfoResult']['status'] - elif operation == 'restore_detail': - return reply['restoreInfoResult']['status'] - elif operation == 'clone_detail': - return int(reply['result']) - else: - msg = (_("Unknown operation " - "%s."), operation) - LOG.error(msg) - raise exception.VolumeBackendAPIException(data=msg) - - def _monitor_request(self, timeout): - """Monitor the request.""" - timer = loopingcall.FixedIntervalLoopingCall( - self._retry_get_detail, - self.start_time, - timeout, - self.function, - self.param) - timer.start(interval=self.configuration.disco_retry_interval).wait() diff --git a/cinder/volume/drivers/disco/disco_api.py b/cinder/volume/drivers/disco/disco_api.py deleted file mode 100644 index 7678b7df212..00000000000 --- a/cinder/volume/drivers/disco/disco_api.py +++ /dev/null @@ -1,165 +0,0 @@ -# copyright (c) 2016 Industrial Technology Research Institute. -# 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. - -"""DISCO Backup Service Implementation.""" - -import json - -import requests -import six - - -class DiscoApi(object): - """Class for all the requests to Disco API.""" - - def __init__(self, ip, port): - """Init client.""" - # Rest related variables - self.req_headers = {'Content-type': 'application/json'} - prefix_vars = {'server_ip': ip, - 'server_port': port, - 'api_prefix': 'RM-REST-Server/disco'} - self.request_prefix = ("http://%(server_ip)s:%(server_port)s" - "/%(api_prefix)s") % prefix_vars - self.prefix_var = {'req_prefix': self.request_prefix} - - def volumeCreate(self, volume_name, size): - """Create a DISCO volume.""" - params = {'volumeName': volume_name, 'volumeSize': size, - 'backupPolicyId': -1} - data = json.dumps(params, - sort_keys=True, - indent=4, - separators=(',', ': ')) - request = ("%(req_prefix)s/volume" % self.prefix_var) - r = requests.post(request, data, headers=self.req_headers) - return r.json() - - def volumeDelete(self, volume_id): - """Delete the temporary volume.""" - request_vars = {'req_prefix': self.request_prefix, - 'volume_id': six.text_type(volume_id)} - request = ("%(req_prefix)s/volume/%(volume_id)s") % request_vars - r = requests.delete(request) - return r.json() - - def volumeExtend(self, vol_id, size): - """Extend DISCO volume.""" - params = {'volumeSize': six.text_type(size), - 'volumeId': six.text_type(vol_id)} - data = json.dumps(params, - sort_keys=True, - indent=4, - separators=(',', ': ')) - request = ("%(req_prefix)s/volume/extend" % self.prefix_var) - r = requests.put(request, data, headers=self.req_headers) - return r.json() - - def volumeDetail(self, volume_id): - """Get volume information of the destination DISCO volume.""" - request_vars = {'req_prefix': self.request_prefix, - 'vol_id': six.text_type(volume_id)} - request = ("%(req_prefix)s/volume/%(vol_id)s") % request_vars - r = requests.get(request) - volume_info = r.json() - return volume_info - - def volumeDetailByName(self, volume_name): - """Get volume information of the DISCO volume.""" - request_vars = {'req_prefix': self.request_prefix, - 'volume_name': six.text_type(volume_name)} - request = ("%(req_prefix)s/volume?name=%(volume_name)s") % request_vars - r = requests.get(request) - return r.json() - - def volumeClone(self, volume_id, volume_name): - """Clone a DISCO volume.""" - params = {'volumeName': volume_name, 'volumeId': volume_id} - data = json.dumps(params, - sort_keys=True, - indent=4, - separators=(',', ': ')) - request = ("%(req_prefix)s/clone" % self.prefix_var) - r = requests.post(request, data, headers=self.req_headers) - return r.json() - - def cloneDetail(self, clone_id, clone_name): - """Get detail of the clone.""" - request_vars = {'req_prefix': self.request_prefix, - 'clone_name': clone_name, - 'clone_id': six.text_type(clone_id)} - request = ("%(req_prefix)s/clone?cloneId=%(clone_id)s&" - "name=%(clone_name)s") % request_vars - r = requests.get(request) - return r.json() - - def snapshotCreate(self, disco_volume_id, reserve_days, zone_id=None, - description=None): - """Take a snapshot of the volume.""" - params = {'volumeId': disco_volume_id, - 'reserveDays': reserve_days, - 'description': description} - data = json.dumps(params, sort_keys=True, indent=4, - separators=(',', ': ')) - - request = ("%(req_prefix)s/snapshot" % self.prefix_var) - r = requests.post(request, data, headers=self.req_headers) - return r.json() - - def snapshotDelete(self, snapshot_id): - """Delete a snapshot.""" - request_vars = {'req_prefix': self.request_prefix, - 'snapshot_id': six.text_type(snapshot_id)} - request = ("%(req_prefix)s/snapshot/%(snapshot_id)s") % request_vars - r = requests.delete(request) - return r.json() - - def snapshotDetail(self, snapshot_id): - """Monitor end of the snapshot.""" - request_vars = {'req_prefix': self.request_prefix, - 'snapshot_id': snapshot_id} - request = ("%(req_prefix)s/snapshot/%(snapshot_id)s") % request_vars - r = requests.get(request) - return r.json() - - def restoreFromSnapshot(self, snapshot_id, volume_name, zone_id, - description, volume_id): - """restore a snapshot of into a volume.""" - params = {'snapshotId': snapshot_id, - 'volumeName': volume_name, - 'zone_id': zone_id, - 'description': "local restore snapshot", - 'volumeId': volume_id} - data = json.dumps(params, - sort_keys=True, - indent=4, - separators=(',', ': ')) - request = ("%(req_prefix)s/restore" % self.prefix_var) - r = requests.post(request, data, headers=self.req_headers) - return r.json() - - def restoreDetail(self, restore_id): - """Monitor end of the restore.""" - request_vars = {'req_prefix': self.request_prefix, - 'restore_id': restore_id} - request = ("%(req_prefix)s/restore/%(restore_id)s") % request_vars - r = requests.get(request) - return r.json() - - def systemInformationList(self): - """Get the list of the system information.""" - request = ("%(req_prefix)s/systemInformationList") % self.prefix_var - r = requests.get(request) - return r.json() diff --git a/cinder/volume/drivers/disco/disco_attach_detach.py b/cinder/volume/drivers/disco/disco_attach_detach.py deleted file mode 100644 index 2148c07b405..00000000000 --- a/cinder/volume/drivers/disco/disco_attach_detach.py +++ /dev/null @@ -1,69 +0,0 @@ -# copyright (c) 2016 Industrial Technology Research Institute. -# 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. - -"""Class for DISCO to attach and detach volume.""" - -from os_brick import initiator -from os_brick.initiator import connector -from oslo_log import log as logging - -from cinder import utils - - -LOG = logging.getLogger(__name__) - - -class AttachDetachDiscoVolume(object): - """Class for attach and detach a DISCO volume.""" - - def __init__(self, configuration): - """Init volume attachment class.""" - self.configuration = configuration - self.connector = connector.InitiatorConnector.factory( - self._get_connector_identifier(), utils.get_root_helper(), - device_scan_attempts=( - self.configuration.num_volume_device_scan_tries) - ) - self.connection_conf = {} - self.connection_conf['server_ip'] = self.configuration.disco_client - self.connection_conf['server_port'] = ( - self.configuration.disco_client_port) - - self.connection_properties = {} - self.connection_properties['name'] = None - self.connection_properties['disco_id'] = None - self.connection_properties['conf'] = self.connection_conf - - def _get_connection_properties(self, volume): - """Return a dictionnary with the connection properties.""" - connection_properties = dict(self.connection_properties) - connection_properties['name'] = volume['name'] - connection_properties['disco_id'] = volume['provider_location'] - return connection_properties - - def _get_connector_identifier(self): - """Return connector identifier, put here to mock it in unit tests.""" - return initiator.DISCO - - def _attach_volume(self, volume): - """Call the connector.connect_volume().""" - connection_properties = self._get_connection_properties(volume) - device_info = self.connector.connect_volume(connection_properties) - return device_info - - def _detach_volume(self, volume): - """Call the connector.disconnect_volume().""" - connection_properties = self._get_connection_properties(volume) - self.connector.disconnect_volume(connection_properties, volume) diff --git a/doc/source/configuration/block-storage/drivers/itri-disco-driver.rst b/doc/source/configuration/block-storage/drivers/itri-disco-driver.rst deleted file mode 100644 index 48415bfe432..00000000000 --- a/doc/source/configuration/block-storage/drivers/itri-disco-driver.rst +++ /dev/null @@ -1,27 +0,0 @@ -======================== -ITRI DISCO volume driver -======================== - -Supported operations -~~~~~~~~~~~~~~~~~~~~ - -The DISCO driver supports the following features: - -* Volume create and delete -* Volume attach and detach -* Snapshot create and delete -* Create volume from snapshot -* Get volume stats -* Copy image to volume -* Copy volume to image -* Clone volume -* Extend volume -* Manage and unmanage volume - -Configuration options -~~~~~~~~~~~~~~~~~~~~~ - -.. config-table:: - :config-target: Disco - - cinder.volume.drivers.disco.disco diff --git a/doc/source/configuration/block-storage/volume-drivers.rst b/doc/source/configuration/block-storage/volume-drivers.rst index 47f2b35eec9..313034de689 100644 --- a/doc/source/configuration/block-storage/volume-drivers.rst +++ b/doc/source/configuration/block-storage/volume-drivers.rst @@ -52,7 +52,6 @@ Driver Configuration Reference drivers/ibm-storwize-svc-driver drivers/infinidat-volume-driver drivers/inspur-instorage-driver - drivers/itri-disco-driver drivers/kaminario-driver drivers/lenovo-driver drivers/nec-storage-m-series-driver diff --git a/doc/source/reference/support-matrix.ini b/doc/source/reference/support-matrix.ini index 1d162bb59e7..77b7bf1bf42 100644 --- a/doc/source/reference/support-matrix.ini +++ b/doc/source/reference/support-matrix.ini @@ -105,9 +105,6 @@ title=Infinidat Storage Driver (iSCSI, FC) [driver.inspur] title=Inspur G2 Storage Driver (iSCSI, FC) -[driver.itri_disco] -title=ITRI DISCO Driver (DISCO) - [driver.kaminario] title=Kaminario Storage Driver (iSCSI, FC) @@ -235,7 +232,6 @@ driver.ibm_gpfs=complete driver.ibm_storwize=complete driver.ibm_xiv=complete driver.inspur=complete -driver.itri_disco=missing driver.kaminario=complete driver.lenovo=complete driver.linbit_drbd=complete @@ -301,7 +297,6 @@ driver.ibm_gpfs=complete driver.ibm_storwize=complete driver.ibm_xiv=complete driver.inspur=complete -driver.itri_disco=complete driver.kaminario=complete driver.lenovo=complete driver.linbit_drbd=complete @@ -367,7 +362,6 @@ driver.ibm_gpfs=missing driver.ibm_storwize=complete driver.ibm_xiv=missing driver.inspur=complete -driver.itri_disco=missing driver.kaminario=missing driver.lenovo=missing driver.linbit_drbd=missing @@ -434,7 +428,6 @@ driver.ibm_gpfs=missing driver.ibm_storwize=complete driver.ibm_xiv=missing driver.inspur=complete -driver.itri_disco=missing driver.kaminario=missing driver.lenovo=missing driver.linbit_drbd=missing @@ -502,7 +495,6 @@ driver.ibm_gpfs=missing driver.ibm_storwize=complete driver.ibm_xiv=complete driver.inspur=complete -driver.itri_disco=missing driver.kaminario=complete driver.lenovo=missing driver.linbit_drbd=missing @@ -571,7 +563,6 @@ driver.ibm_gpfs=missing driver.ibm_storwize=complete driver.ibm_xiv=complete driver.inspur=complete -driver.itri_disco=missing driver.kaminario=missing driver.lenovo=missing driver.linbit_drbd=missing @@ -639,7 +630,6 @@ driver.ibm_gpfs=missing driver.ibm_storwize=missing driver.ibm_xiv=missing driver.inspur=missing -driver.itri_disco=missing driver.kaminario=complete driver.lenovo=missing driver.linbit_drbd=missing @@ -708,7 +698,6 @@ driver.ibm_gpfs=missing driver.ibm_storwize=missing driver.ibm_xiv=missing driver.inspur=missing -driver.itri_disco=missing driver.kaminario=missing driver.lenovo=missing driver.linbit_drbd=missing @@ -777,7 +766,6 @@ driver.ibm_gpfs=missing driver.ibm_storwize=complete driver.ibm_xiv=complete driver.inspur=missing -driver.itri_disco=missing driver.kaminario=missing driver.lenovo=missing driver.linbit_drbd=missing diff --git a/doc/source/reference/support-matrix.rst b/doc/source/reference/support-matrix.rst index 42d53a34c34..772fa2bd104 100644 --- a/doc/source/reference/support-matrix.rst +++ b/doc/source/reference/support-matrix.rst @@ -68,3 +68,4 @@ release. * Stein * HGST Flash Storage Suite Driver (vgc) + * ITRI DISCO Driver diff --git a/releasenotes/notes/itri-disco-driver-removal-11e14fbf431ea876.yaml b/releasenotes/notes/itri-disco-driver-removal-11e14fbf431ea876.yaml new file mode 100644 index 00000000000..30a0390aea5 --- /dev/null +++ b/releasenotes/notes/itri-disco-driver-removal-11e14fbf431ea876.yaml @@ -0,0 +1,15 @@ +--- +upgrade: + - | + The ITRI DISCO storage driver has been removed after completion of its + deprecation period without a reliable 3rd Party CI system being + supported. Customers using the ITRI DISCO driver should not upgrade + Cinder without first migrating all volumes from their DISCO backend + to a supported storage backend. Failure to migrate volumes will + result in no longer being able to access volumes back by the ITRI DISCO + storage backend. +other: + - | + The ITRI DISCO storage driver was marked unsupported in Rocky due to + 3rd Party CI not meeting Cinder's requirements. As a result the + driver is removed starting from the Stein release.