From ce8efd1c39b43998e2c4573add632f0409f21de2 Mon Sep 17 00:00:00 2001 From: Navneet Singh Date: Tue, 12 Aug 2014 13:52:39 +0530 Subject: [PATCH] Manage/unmanage impl for NetApp ONTAP iscsi driver This patch implements manage and unmanage support for NetApp's cmode and 7mode iscsi drivers. The input required for manage call is either source-id or source-name or both. Implements: Blueprint cdot-iscsi-manage-unmanage Implements: Blueprint 7mode-iscsi-manage-unmanage Change-Id: Ifdc700122b98a2fb6cbd53d913d72391d7e92b54 --- .../netapp/dataontap/test_block_7mode.py | 13 +++ .../netapp/dataontap/test_block_base.py | 85 ++++++++++++++ .../netapp/dataontap/test_block_cmode.py | 46 ++++++++ .../drivers/netapp/dataontap/block_7mode.py | 17 +++ .../drivers/netapp/dataontap/block_base.py | 106 ++++++++++++++++-- .../drivers/netapp/dataontap/block_cmode.py | 43 ++++++- .../netapp/dataontap/client/client_cmode.py | 9 ++ .../drivers/netapp/dataontap/iscsi_7mode.py | 9 ++ .../drivers/netapp/dataontap/iscsi_cmode.py | 9 ++ 9 files changed, 329 insertions(+), 8 deletions(-) diff --git a/cinder/tests/volume/drivers/netapp/dataontap/test_block_7mode.py b/cinder/tests/volume/drivers/netapp/dataontap/test_block_7mode.py index aee2a3b5d..43d90d725 100644 --- a/cinder/tests/volume/drivers/netapp/dataontap/test_block_7mode.py +++ b/cinder/tests/volume/drivers/netapp/dataontap/test_block_7mode.py @@ -27,11 +27,13 @@ import cinder.tests.volume.drivers.netapp.fakes as na_fakes from cinder.volume.drivers.netapp.dataontap import block_7mode from cinder.volume.drivers.netapp.dataontap.block_7mode import \ NetAppBlockStorage7modeLibrary as block_lib_7mode +from cinder.volume.drivers.netapp.dataontap import block_base from cinder.volume.drivers.netapp.dataontap.block_base import \ NetAppBlockStorageLibrary as block_lib from cinder.volume.drivers.netapp.dataontap.client import api as netapp_api from cinder.volume.drivers.netapp.dataontap.client.api import NaApiError from cinder.volume.drivers.netapp.dataontap.client import client_base +from cinder.volume.drivers.netapp import utils as na_utils class NetAppBlockStorage7modeLibraryTestCase(test.TestCase): @@ -352,3 +354,14 @@ class NetAppBlockStorage7modeLibraryTestCase(test.TestCase): self.library.zapi_client.create_lun.assert_called_once_with( fake.VOLUME, fake.LUN, fake.SIZE, fake.METADATA, None) self.assertTrue(self.library.vol_refresh_voluntary) + + @mock.patch.object(na_utils, 'get_volume_extra_specs') + def test_check_volume_type_for_lun_qos_not_supported(self, get_specs): + get_specs.return_value = {'specs': 's', + 'netapp:qos_policy_group': 'qos'} + mock_lun = block_base.NetAppLun('handle', 'name', '1', + {'Volume': 'name', 'Path': '/vol/lun'}) + self.assertRaises(exception.ManageExistingVolumeTypeMismatch, + self.library._check_volume_type_for_lun, + {'vol': 'vol'}, mock_lun, {'ref': 'ref'}) + get_specs.assert_called_once_with({'vol': 'vol'}) diff --git a/cinder/tests/volume/drivers/netapp/dataontap/test_block_base.py b/cinder/tests/volume/drivers/netapp/dataontap/test_block_base.py index 4d1995c02..a84780051 100644 --- a/cinder/tests/volume/drivers/netapp/dataontap/test_block_base.py +++ b/cinder/tests/volume/drivers/netapp/dataontap/test_block_base.py @@ -72,6 +72,7 @@ class NetAppBlockStorageLibraryTestCase(test.TestCase): mock.Mock(return_value=None)) @mock.patch.object(block_base, 'LOG', mock.Mock()) def test_create_volume(self): + self.library.zapi_client.get_lun_by_args.return_value = ['lun'] self.library.create_volume({'name': 'lun1', 'size': 100, 'id': uuid.uuid4(), 'host': 'hostname@backend#vol1'}) @@ -289,6 +290,7 @@ class NetAppBlockStorageLibraryTestCase(test.TestCase): @mock.patch.object(na_utils, 'get_volume_extra_specs', mock.Mock(return_value={'netapp:raid_type': 'raid4'})) def test_create_volume_obsolete_extra_spec(self): + self.library.zapi_client.get_lun_by_args.return_value = ['lun'] self.library.create_volume({'name': 'lun1', 'size': 100, 'id': uuid.uuid4(), @@ -306,6 +308,7 @@ class NetAppBlockStorageLibraryTestCase(test.TestCase): mock.Mock(return_value={'netapp_thick_provisioned': 'true'})) def test_create_volume_deprecated_extra_spec(self): + self.library.zapi_client.get_lun_by_args.return_value = ['lun'] self.library.create_volume({'name': 'lun1', 'size': 100, 'id': uuid.uuid4(), @@ -320,3 +323,85 @@ class NetAppBlockStorageLibraryTestCase(test.TestCase): self.library.do_setup(mock.Mock()) self.assertTrue(mock_check_flags.called) + + def test_get_existing_vol_manage_missing_id_path(self): + self.assertRaises(exception.ManageExistingInvalidReference, + self.library._get_existing_vol_with_manage_ref, + {}) + + def test_get_existing_vol_manage_not_found(self): + self.zapi_client.get_lun_by_args.return_value = [] + self.assertRaises(exception.ManageExistingInvalidReference, + self.library._get_existing_vol_with_manage_ref, + {'source-id': 'src_id', + 'source-name': 'lun_path'}) + self.assertEqual(1, self.zapi_client.get_lun_by_args.call_count) + + @mock.patch.object(block_lib, '_extract_lun_info', + mock.Mock(return_value=block_base.NetAppLun( + 'lun0', 'lun0', '3', {'UUID': 'src_id'}))) + def test_get_existing_vol_manage_lun(self): + self.zapi_client.get_lun_by_args.return_value = ['lun0', 'lun1'] + lun = self.library._get_existing_vol_with_manage_ref( + {'source-id': 'src_id', 'path': 'lun_path'}) + self.assertEqual(1, self.zapi_client.get_lun_by_args.call_count) + self.library._extract_lun_info.assert_called_once_with('lun0') + self.assertEqual('lun0', lun.name) + + @mock.patch.object(block_lib, '_get_existing_vol_with_manage_ref', + mock.Mock(return_value=block_base.NetAppLun( + 'handle', 'name', '1073742824', {}))) + def test_manage_existing_get_size(self): + size = self.library.manage_existing_get_size( + {'id': 'vol_id'}, {'ref': 'ref'}) + self.assertEqual(2, size) + self.library._get_existing_vol_with_manage_ref.assert_called_once_with( + {'ref': 'ref'}) + + @mock.patch.object(block_base.LOG, 'info') + def test_unmanage(self, log): + mock_lun = block_base.NetAppLun('handle', 'name', '1', + {'Path': 'p', 'UUID': 'uuid'}) + self.library._get_lun_from_table = mock.Mock(return_value=mock_lun) + self.library.unmanage({'name': 'vol'}) + self.library._get_lun_from_table.assert_called_once_with('vol') + self.assertEqual(1, log.call_count) + + def test_manage_existing_lun_same_name(self): + mock_lun = block_base.NetAppLun('handle', 'name', '1', + {'Path': '/vol/vol1/name'}) + self.library._get_existing_vol_with_manage_ref = mock.Mock( + return_value=mock_lun) + self.library._check_volume_type_for_lun = mock.Mock() + self.library._add_lun_to_table = mock.Mock() + self.zapi_client.move_lun = mock.Mock() + self.library.manage_existing({'name': 'name'}, {'ref': 'ref'}) + self.library._get_existing_vol_with_manage_ref.assert_called_once_with( + {'ref': 'ref'}) + self.assertEqual(1, self.library._check_volume_type_for_lun.call_count) + self.assertEqual(1, self.library._add_lun_to_table.call_count) + self.assertEqual(0, self.zapi_client.move_lun.call_count) + + def test_manage_existing_lun_new_path(self): + mock_lun = block_base.NetAppLun( + 'handle', 'name', '1', {'Path': '/vol/vol1/name'}) + self.library._get_existing_vol_with_manage_ref = mock.Mock( + return_value=mock_lun) + self.library._check_volume_type_for_lun = mock.Mock() + self.library._add_lun_to_table = mock.Mock() + self.zapi_client.move_lun = mock.Mock() + self.library.manage_existing({'name': 'volume'}, {'ref': 'ref'}) + self.assertEqual( + 2, self.library._get_existing_vol_with_manage_ref.call_count) + self.assertEqual(1, self.library._check_volume_type_for_lun.call_count) + self.assertEqual(1, self.library._add_lun_to_table.call_count) + self.zapi_client.move_lun.assert_called_once_with( + '/vol/vol1/name', '/vol/vol1/volume') + + def test_check_vol_type_for_lun(self): + self.assertRaises(NotImplementedError, + self.library._check_volume_type_for_lun, + 'vol', 'lun', 'existing_ref') + + def test_is_lun_valid_on_storage(self): + self.assertTrue(self.library._is_lun_valid_on_storage('lun')) diff --git a/cinder/tests/volume/drivers/netapp/dataontap/test_block_cmode.py b/cinder/tests/volume/drivers/netapp/dataontap/test_block_cmode.py index 8cf19a549..e15e133ea 100644 --- a/cinder/tests/volume/drivers/netapp/dataontap/test_block_cmode.py +++ b/cinder/tests/volume/drivers/netapp/dataontap/test_block_cmode.py @@ -19,9 +19,11 @@ Mock unit tests for the NetApp block storage C-mode library import mock +from cinder import exception from cinder import test import cinder.tests.volume.drivers.netapp.dataontap.fakes as fake import cinder.tests.volume.drivers.netapp.fakes as na_fakes +from cinder.volume.drivers.netapp.dataontap import block_base from cinder.volume.drivers.netapp.dataontap.block_base import \ NetAppBlockStorageLibrary as block_lib from cinder.volume.drivers.netapp.dataontap import block_cmode @@ -217,3 +219,47 @@ class NetAppBlockStorageCmodeLibraryTestCase(test.TestCase): self.library.zapi_client.create_lun.assert_called_once_with( fake.VOLUME, fake.LUN, fake.SIZE, fake.METADATA, None) self.assertEqual(1, self.library._update_stale_vols.call_count) + + @mock.patch.object(ssc_cmode, 'get_volumes_for_specs') + @mock.patch.object(ssc_cmode, 'get_cluster_latest_ssc') + @mock.patch.object(na_utils, 'get_volume_extra_specs') + def test_check_volume_type_for_lun_fail( + self, get_specs, get_ssc, get_vols): + self.library.ssc_vols = ['vol'] + get_specs.return_value = {'specs': 's'} + get_vols.return_value = [ssc_cmode.NetAppVolume(name='name', + vserver='vs')] + mock_lun = block_base.NetAppLun('handle', 'name', '1', + {'Volume': 'fake', 'Path': '/vol/lun'}) + self.assertRaises(exception.ManageExistingVolumeTypeMismatch, + self.library._check_volume_type_for_lun, + {'vol': 'vol'}, mock_lun, {'ref': 'ref'}) + get_specs.assert_called_once_with({'vol': 'vol'}) + get_vols.assert_called_with(['vol'], {'specs': 's'}) + self.assertEqual(1, get_ssc.call_count) + + @mock.patch.object(block_cmode.LOG, 'error') + @mock.patch.object(ssc_cmode, 'get_volumes_for_specs') + @mock.patch.object(ssc_cmode, 'get_cluster_latest_ssc') + @mock.patch.object(na_utils, 'get_volume_extra_specs') + def test_check_volume_type_for_lun_qos_fail( + self, get_specs, get_ssc, get_vols, driver_log): + self.zapi_client.connection.set_api_version(1, 20) + self.library.ssc_vols = ['vol'] + get_specs.return_value = {'specs': 's', + 'netapp:qos_policy_group': 'qos'} + get_vols.return_value = [ssc_cmode.NetAppVolume(name='name', + vserver='vs')] + mock_lun = block_base.NetAppLun('handle', 'name', '1', + {'Volume': 'name', 'Path': '/vol/lun'}) + self.zapi_client.set_lun_qos_policy_group = mock.Mock( + side_effect=netapp_api.NaApiError) + self.assertRaises(exception.ManageExistingVolumeTypeMismatch, + self.library._check_volume_type_for_lun, + {'vol': 'vol'}, mock_lun, {'ref': 'ref'}) + get_specs.assert_called_once_with({'vol': 'vol'}) + get_vols.assert_called_with(['vol'], {'specs': 's'}) + self.assertEqual(0, get_ssc.call_count) + self.zapi_client.set_lun_qos_policy_group.assert_called_once_with( + '/vol/lun', 'qos') + self.assertEqual(1, driver_log.call_count) diff --git a/cinder/volume/drivers/netapp/dataontap/block_7mode.py b/cinder/volume/drivers/netapp/dataontap/block_7mode.py index 7a371db38..75ac4bef1 100644 --- a/cinder/volume/drivers/netapp/dataontap/block_7mode.py +++ b/cinder/volume/drivers/netapp/dataontap/block_7mode.py @@ -207,6 +207,7 @@ class NetAppBlockStorage7modeLibrary(block_base. meta_dict['OsType'] = lun.get_child_content('multiprotocol-type') meta_dict['SpaceReserved'] = lun.get_child_content( 'is-space-reservation-enabled') + meta_dict['UUID'] = lun.get_child_content('uuid') return meta_dict def _get_fc_target_wwpns(self, include_partner=True): @@ -320,3 +321,19 @@ class NetAppBlockStorage7modeLibrary(block_base. """Driver entry point for destroying existing volumes.""" super(NetAppBlockStorage7modeLibrary, self).delete_volume(volume) self.vol_refresh_voluntary = True + + def _is_lun_valid_on_storage(self, lun): + """Validate LUN specific to storage system.""" + if self.volume_list: + lun_vol = lun.get_metadata_property('Volume') + if lun_vol not in self.volume_list: + return False + return True + + def _check_volume_type_for_lun(self, volume, lun, existing_ref): + """Check if lun satisfies volume type.""" + extra_specs = na_utils.get_volume_extra_specs(volume) + if extra_specs and extra_specs.pop('netapp:qos_policy_group', None): + raise exception.ManageExistingVolumeTypeMismatch( + reason=_("Setting LUN QoS policy group is not supported" + " on this storage family and ONTAP version.")) diff --git a/cinder/volume/drivers/netapp/dataontap/block_base.py b/cinder/volume/drivers/netapp/dataontap/block_base.py index 446c7a755..bd4406a4c 100644 --- a/cinder/volume/drivers/netapp/dataontap/block_base.py +++ b/cinder/volume/drivers/netapp/dataontap/block_base.py @@ -21,6 +21,7 @@ Volume driver library for NetApp 7/C-mode block storage systems. """ +import math import sys import uuid @@ -84,6 +85,7 @@ class NetAppBlockStorageLibrary(object): self.lun_table = {} self.lookup_service = fczm_utils.create_lookup_service() self.app_version = kwargs.get("app_version", "unknown") + self.db = kwargs.get('db') self.configuration = kwargs['configuration'] self.configuration.append_config_values(na_opts.netapp_connection_opts) @@ -135,7 +137,9 @@ class NetAppBlockStorageLibrary(object): size = default_size if not int(volume['size'])\ else int(volume['size']) * units.Gi - metadata = {'OsType': 'linux', 'SpaceReserved': 'true'} + metadata = {'OsType': 'linux', + 'SpaceReserved': 'true', + 'Path': '/vol/%s/%s' % (ontap_volume_name, lun_name)} extra_specs = na_utils.get_volume_extra_specs(volume) qos_policy_group = extra_specs.pop('netapp:qos_policy_group', None) \ @@ -233,16 +237,21 @@ class NetAppBlockStorageLibrary(object): """Returns LUN handle based on filer type.""" raise NotImplementedError() + def _extract_lun_info(self, lun): + """Extracts the LUNs from API and populates the LUN table.""" + + meta_dict = self._create_lun_meta(lun) + path = lun.get_child_content('path') + (_rest, _splitter, name) = path.rpartition('/') + handle = self._create_lun_handle(meta_dict) + size = lun.get_child_content('size') + return NetAppLun(handle, name, size, meta_dict) + def _extract_and_populate_luns(self, api_luns): """Extracts the LUNs from API and populates the LUN table.""" for lun in api_luns: - meta_dict = self._create_lun_meta(lun) - path = lun.get_child_content('path') - (_rest, _splitter, name) = path.rpartition('/') - handle = self._create_lun_handle(meta_dict) - size = lun.get_child_content('size') - discovered_lun = NetAppLun(handle, name, size, meta_dict) + discovered_lun = self._extract_lun_info(lun) self._add_lun_to_table(discovered_lun) def _map_lun(self, name, initiator_list, initiator_type, lun_id=None): @@ -501,6 +510,89 @@ class NetAppBlockStorageLibrary(object): block_count = ls / bs return block_count + def _check_volume_type_for_lun(self, volume, lun, existing_ref): + """Checks if lun satifies the volume type.""" + raise NotImplementedError() + + def manage_existing(self, volume, existing_ref): + """Brings an existing storage object under Cinder management. + + existing_ref can contain source-id or source-name or both. + source-id: lun uuid. + source-name: complete lun path eg. /vol/vol0/lun. + """ + lun = self._get_existing_vol_with_manage_ref(existing_ref) + self._check_volume_type_for_lun(volume, lun, existing_ref) + path = lun.get_metadata_property('Path') + if lun.name == volume['name']: + LOG.info(_LI("LUN with given ref %s need not be renamed " + "during manage operation."), existing_ref) + else: + (rest, splitter, name) = path.rpartition('/') + new_path = '%s/%s' % (rest, volume['name']) + self.zapi_client.move_lun(path, new_path) + lun = self._get_existing_vol_with_manage_ref( + {'source-name': new_path}) + self._add_lun_to_table(lun) + LOG.info(_LI("Manage operation completed for LUN with new path" + " %(path)s and uuid %(uuid)s."), + {'path': lun.get_metadata_property('Path'), + 'uuid': lun.get_metadata_property('UUID')}) + + def manage_existing_get_size(self, volume, existing_ref): + """Return size of volume to be managed by manage_existing. + + When calculating the size, round up to the next GB. + """ + lun = self._get_existing_vol_with_manage_ref(existing_ref) + return int(math.ceil(float(lun.size) / units.Gi)) + + def _get_existing_vol_with_manage_ref(self, existing_ref): + """Get the corresponding LUN from the storage server.""" + uuid = existing_ref.get('source-id') + path = existing_ref.get('source-name') + if not (uuid or path): + reason = _('Reference must contain either source-id' + ' or source-name element.') + raise exception.ManageExistingInvalidReference( + existing_ref=existing_ref, reason=reason) + lun_info = {} + lun_info.setdefault('path', path) if path else None + if hasattr(self, 'vserver') and uuid: + lun_info['uuid'] = uuid + luns = self.zapi_client.get_lun_by_args(**lun_info) + if luns: + for lun in luns: + netapp_lun = self._extract_lun_info(lun) + storage_valid = self._is_lun_valid_on_storage(netapp_lun) + uuid_valid = True + if uuid: + if netapp_lun.get_metadata_property('UUID') == uuid: + uuid_valid = True + else: + uuid_valid = False + if storage_valid and uuid_valid: + return netapp_lun + raise exception.ManageExistingInvalidReference( + existing_ref=existing_ref, + reason=(_('LUN not found with given ref %s.') % existing_ref)) + + def _is_lun_valid_on_storage(self, lun): + """Validate lun specific to storage system.""" + return True + + def unmanage(self, volume): + """Removes the specified volume from Cinder management. + + Does not delete the underlying backend storage object. + """ + managed_lun = self._get_lun_from_table(volume['name']) + LOG.info(_LI("Unmanaged LUN with current path %(path)s and uuid " + "%(uuid)s."), + {'path': managed_lun.get_metadata_property('Path'), + 'uuid': managed_lun.get_metadata_property('UUID') + or 'unknown'}) + def initialize_connection_iscsi(self, volume, connector): """Driver entry point to attach a volume to an instance. diff --git a/cinder/volume/drivers/netapp/dataontap/block_cmode.py b/cinder/volume/drivers/netapp/dataontap/block_cmode.py index 6d944428c..d7468509a 100644 --- a/cinder/volume/drivers/netapp/dataontap/block_cmode.py +++ b/cinder/volume/drivers/netapp/dataontap/block_cmode.py @@ -27,10 +27,11 @@ from oslo_utils import units import six from cinder import exception -from cinder.i18n import _ +from cinder.i18n import _, _LE from cinder.openstack.common import log as logging from cinder import utils from cinder.volume.drivers.netapp.dataontap import block_base +from cinder.volume.drivers.netapp.dataontap.client import api as netapp_api from cinder.volume.drivers.netapp.dataontap.client import client_cmode from cinder.volume.drivers.netapp.dataontap import ssc_cmode from cinder.volume.drivers.netapp import options as na_opts @@ -138,6 +139,7 @@ class NetAppBlockStorageCmodeLibrary(block_base. meta_dict['OsType'] = lun.get_child_content('multiprotocol-type') meta_dict['SpaceReserved'] = \ lun.get_child_content('is-space-reservation-enabled') + meta_dict['UUID'] = lun.get_child_content('uuid') return meta_dict def _get_fc_target_wwpns(self, include_partner=True): @@ -242,3 +244,42 @@ class NetAppBlockStorageCmodeLibrary(block_base. if netapp_vol: self._update_stale_vols( volume=ssc_cmode.NetAppVolume(netapp_vol, self.vserver)) + + def _check_volume_type_for_lun(self, volume, lun, existing_ref): + """Check if LUN satisfies volume type.""" + extra_specs = na_utils.get_volume_extra_specs(volume) + match_write = False + + def scan_ssc_data(): + volumes = ssc_cmode.get_volumes_for_specs(self.ssc_vols, + extra_specs) + for vol in volumes: + if lun.get_metadata_property('Volume') == vol.id['name']: + return True + return False + + match_read = scan_ssc_data() + if not match_read: + ssc_cmode.get_cluster_latest_ssc( + self, self.zapi_client.get_connection(), self.vserver) + match_read = scan_ssc_data() + + qos_policy_group = extra_specs.pop('netapp:qos_policy_group', None) \ + if extra_specs else None + if qos_policy_group: + if match_read: + try: + path = lun.get_metadata_property('Path') + self.zapi_client.set_lun_qos_policy_group(path, + qos_policy_group) + match_write = True + except netapp_api.NaApiError as nae: + LOG.error(_LE("Failure setting QoS policy group. %s"), nae) + else: + match_write = True + if not (match_read and match_write): + raise exception.ManageExistingVolumeTypeMismatch( + reason=(_("LUN with given ref %(ref)s does not satisfy volume" + " type. Ensure LUN volume with ssc features is" + " present on vserver %(vs)s.") + % {'ref': existing_ref, 'vs': self.vserver})) diff --git a/cinder/volume/drivers/netapp/dataontap/client/client_cmode.py b/cinder/volume/drivers/netapp/dataontap/client/client_cmode.py index e63ee82a5..63a03980e 100644 --- a/cinder/volume/drivers/netapp/dataontap/client/client_cmode.py +++ b/cinder/volume/drivers/netapp/dataontap/client/client_cmode.py @@ -291,6 +291,8 @@ class Client(client_base.Client): query.add_node_with_children('lun-info', **args) luns = self.connection.invoke_successfully(lun_iter, True) attr_list = luns.get_child_by_name('attributes-list') + if not attr_list: + return [] return attr_list.get_children() def file_assign_qos(self, flex_vol, qos_policy_group, file_path): @@ -303,6 +305,13 @@ class Client(client_base.Client): 'vserver': self.vserver}) self.connection.invoke_successfully(file_assign_qos, True) + def set_lun_qos_policy_group(self, path, qos_policy_group): + """Sets qos_policy_group on a LUN.""" + set_qos_group = netapp_api.NaElement.create_node_with_children( + 'lun-set-qos-policy-group', + **{'path': path, 'qos-policy-group': qos_policy_group}) + self.connection.invoke_successfully(set_qos_group, True) + def get_if_info_by_ip(self, ip): """Gets the network interface info by ip.""" net_if_iter = netapp_api.NaElement('net-interface-get-iter') diff --git a/cinder/volume/drivers/netapp/dataontap/iscsi_7mode.py b/cinder/volume/drivers/netapp/dataontap/iscsi_7mode.py index db35c163d..369ed9516 100644 --- a/cinder/volume/drivers/netapp/dataontap/iscsi_7mode.py +++ b/cinder/volume/drivers/netapp/dataontap/iscsi_7mode.py @@ -72,6 +72,15 @@ class NetApp7modeISCSIDriver(driver.ISCSIDriver): def remove_export(self, context, volume): self.library.remove_export(context, volume) + def manage_existing(self, volume, existing_ref): + return self.library.manage_existing(volume, existing_ref) + + def manage_existing_get_size(self, volume, existing_ref): + return self.library.manage_existing_get_size(volume, existing_ref) + + def unmanage(self, volume): + return self.library.unmanage(volume) + def initialize_connection(self, volume, connector): return self.library.initialize_connection_iscsi(volume, connector) diff --git a/cinder/volume/drivers/netapp/dataontap/iscsi_cmode.py b/cinder/volume/drivers/netapp/dataontap/iscsi_cmode.py index ac3396d86..325322634 100644 --- a/cinder/volume/drivers/netapp/dataontap/iscsi_cmode.py +++ b/cinder/volume/drivers/netapp/dataontap/iscsi_cmode.py @@ -72,6 +72,15 @@ class NetAppCmodeISCSIDriver(driver.ISCSIDriver): def remove_export(self, context, volume): self.library.remove_export(context, volume) + def manage_existing(self, volume, existing_ref): + return self.library.manage_existing(volume, existing_ref) + + def manage_existing_get_size(self, volume, existing_ref): + return self.library.manage_existing_get_size(volume, existing_ref) + + def unmanage(self, volume): + return self.library.unmanage(volume) + def initialize_connection(self, volume, connector): return self.library.initialize_connection_iscsi(volume, connector) -- 2.45.2