]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
Manage/unmanage impl for NetApp ONTAP iscsi driver
authorNavneet Singh <singn@netapp.com>
Tue, 12 Aug 2014 08:22:39 +0000 (13:52 +0530)
committerBob Callaway <bob.callaway@netapp.com>
Thu, 19 Feb 2015 21:43:08 +0000 (21:43 +0000)
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

cinder/tests/volume/drivers/netapp/dataontap/test_block_7mode.py
cinder/tests/volume/drivers/netapp/dataontap/test_block_base.py
cinder/tests/volume/drivers/netapp/dataontap/test_block_cmode.py
cinder/volume/drivers/netapp/dataontap/block_7mode.py
cinder/volume/drivers/netapp/dataontap/block_base.py
cinder/volume/drivers/netapp/dataontap/block_cmode.py
cinder/volume/drivers/netapp/dataontap/client/client_cmode.py
cinder/volume/drivers/netapp/dataontap/iscsi_7mode.py
cinder/volume/drivers/netapp/dataontap/iscsi_cmode.py

index aee2a3b5d5705fee996b22b81748c69057ed200d..43d90d725844ee58b8adb99e1633f6a855929056 100644 (file)
@@ -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'})
index 4d1995c021fa5a6156994bc939a98fcdded84e2a..a847800511f74c97e7d46e8230d349b37e3fc784 100644 (file)
@@ -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'))
index 8cf19a54949139de0a219c002407f68d5627f551..e15e133ea268bcc1db3bd5ba7b9eefcec3a43460 100644 (file)
@@ -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)
index 7a371db386c6bab0f45d0dd9bb0196c6b1839147..75ac4bef1eb6d96eb60acbf0138794c42ca118f6 100644 (file)
@@ -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."))
index 446c7a755bc7b48a7e8778f66a245ff30d32df6a..bd4406a4ca07660490060aebe67612e1c7f0a742 100644 (file)
@@ -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.
 
index 6d944428cad01d6682d0f091c1e9b37cc153e5d3..d7468509af06af6554c399e5ff74ec6b92b58d64 100644 (file)
@@ -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}))
index e63ee82a597e5ece6e3076a588b27c197a7718e9..63a03980e573d3e8f49719bfeaa22637e231b843 100644 (file)
@@ -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')
index db35c163d114058000bc613bd46d2d32eb886e38..369ed95160e41bd9d89e9b7c7aed36118dc2b5dd 100644 (file)
@@ -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)
 
index ac3396d86944c128ad912b83c9592289be68376d..325322634af26fedc759d2519d02986504131ddb 100644 (file)
@@ -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)