--- /dev/null
+# Copyright (c) 2014 NetApp, Inc.
+# 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.
+"""
+Mock unit tests for the NetApp iSCSI driver
+"""
+
+import mock
+
+from cinder import test
+import cinder.volume.drivers.netapp.api as ntapi
+import cinder.volume.drivers.netapp.iscsi as ntap_iscsi
+
+
+class NetAppiSCSICModeTestCase(test.TestCase):
+ """Test case for NetApp's C-Mode iSCSI driver."""
+
+ def setUp(self):
+ super(NetAppiSCSICModeTestCase, self).setUp()
+ self.driver = ntap_iscsi.NetAppDirectCmodeISCSIDriver(
+ configuration=mock.Mock())
+ self.driver.client = mock.Mock()
+ self.driver.vserver = mock.Mock()
+
+ def tearDown(self):
+ super(NetAppiSCSICModeTestCase, self).tearDown()
+
+ def test_clone_lun_multiple_zapi_calls(self):
+ """Test for when lun clone requires more than one zapi call."""
+
+ # Max block-ranges per call = 32, max blocks per range = 2^24
+ # Force 2 calls
+ bc = 2 ** 24 * 32 * 2
+ self.driver._get_lun_attr = mock.Mock(return_value={'Volume':
+ 'fakeLUN'})
+ self.driver.client.invoke_successfully = mock.Mock()
+ lun = ntapi.NaElement.create_node_with_children(
+ 'lun-info',
+ **{'alignment': 'indeterminate',
+ 'block-size': '512',
+ 'comment': '',
+ 'creation-timestamp': '1354536362',
+ 'is-space-alloc-enabled': 'false',
+ 'is-space-reservation-enabled': 'true',
+ 'mapped': 'false',
+ 'multiprotocol-type': 'linux',
+ 'online': 'true',
+ 'path': '/vol/fakeLUN/lun1',
+ 'prefix-size': '0',
+ 'qtree': '',
+ 'read-only': 'false',
+ 'serial-number': '2FfGI$APyN68',
+ 'share-state': 'none',
+ 'size': '20971520',
+ 'size-used': '0',
+ 'staging': 'false',
+ 'suffix-size': '0',
+ 'uuid': 'cec1f3d7-3d41-11e2-9cf4-123478563412',
+ 'volume': 'fakeLUN',
+ 'vserver': 'fake_vserver'})
+ self.driver._get_lun_by_args = mock.Mock(return_value=[lun])
+ self.driver._add_lun_to_table = mock.Mock()
+ self.driver._update_stale_vols = mock.Mock()
+
+ self.driver._clone_lun('fakeLUN', 'newFakeLUN', block_count=bc)
+
+ self.assertEqual(2, self.driver.client.invoke_successfully.call_count)
+
+
+class NetAppiSCSI7ModeTestCase(test.TestCase):
+ """Test case for NetApp's 7-Mode iSCSI driver."""
+
+ def setUp(self):
+ super(NetAppiSCSI7ModeTestCase, self).setUp()
+ self.driver = ntap_iscsi.NetAppDirect7modeISCSIDriver(
+ configuration=mock.Mock())
+ self.driver.client = mock.Mock()
+ self.driver.vfiler = mock.Mock()
+
+ def tearDown(self):
+ super(NetAppiSCSI7ModeTestCase, self).tearDown()
+
+ def test_clone_lun_multiple_zapi_calls(self):
+ """Test for when lun clone requires more than one zapi call."""
+
+ # Max block-ranges per call = 32, max blocks per range = 2^24
+ # Force 2 calls
+ bc = 2 ** 24 * 32 * 2
+ self.driver._get_lun_attr = mock.Mock(return_value={'Volume':
+ 'fakeLUN',
+ 'Path':
+ '/vol/fake/lun1'})
+ self.driver.client.invoke_successfully = mock.Mock(
+ return_value=mock.MagicMock())
+ lun = ntapi.NaElement.create_node_with_children(
+ 'lun-info',
+ **{'alignment': 'indeterminate',
+ 'block-size': '512',
+ 'comment': '',
+ 'creation-timestamp': '1354536362',
+ 'is-space-alloc-enabled': 'false',
+ 'is-space-reservation-enabled': 'true',
+ 'mapped': 'false',
+ 'multiprotocol-type': 'linux',
+ 'online': 'true',
+ 'path': '/vol/fakeLUN/lun1',
+ 'prefix-size': '0',
+ 'qtree': '',
+ 'read-only': 'false',
+ 'serial-number': '2FfGI$APyN68',
+ 'share-state': 'none',
+ 'size': '20971520',
+ 'size-used': '0',
+ 'staging': 'false',
+ 'suffix-size': '0',
+ 'uuid': 'cec1f3d7-3d41-11e2-9cf4-123478563412',
+ 'volume': 'fakeLUN',
+ 'vserver': 'fake_vserver'})
+ self.driver._get_lun_by_args = mock.Mock(return_value=[lun])
+ self.driver._add_lun_to_table = mock.Mock()
+ self.driver._update_stale_vols = mock.Mock()
+ self.driver._check_clone_status = mock.Mock()
+ self.driver._set_space_reserve = mock.Mock()
+
+ self.driver._clone_lun('fakeLUN', 'newFakeLUN', block_count=bc)
+
+ self.assertEqual(2, self.driver.client.invoke_successfully.call_count)
"""Clone LUN with the given handle to the new name."""
metadata = self._get_lun_attr(name, 'metadata')
volume = metadata['Volume']
- clone_create = NaElement.create_node_with_children(
- 'clone-create',
- **{'volume': volume, 'source-path': name,
- 'destination-path': new_name, 'space-reserve': space_reserved})
- if block_count > 0:
- block_ranges = NaElement("block-ranges")
- # zAPI can only handle 2^24 block ranges
- bc_limit = 2 ** 24 # 8GB
- segments = int(math.ceil(float(block_count) / float(bc_limit)))
- bc = block_count
- for segment in range(0, segments):
- if bc > bc_limit:
- block_count = bc_limit
- bc -= bc_limit
- else:
- block_count = bc
- block_range = NaElement.create_node_with_children(
- 'block-range',
- **{'source-block-number': str(src_block),
- 'destination-block-number': str(dest_block),
- 'block-count': str(block_count)})
- block_ranges.add_child_elem(block_range)
- src_block = int(src_block) + int(block_count)
- dest_block = int(dest_block) + int(block_count)
- clone_create.add_child_elem(block_ranges)
- self.client.invoke_successfully(clone_create, True)
+ # zAPI can only handle 2^24 blocks per range
+ bc_limit = 2 ** 24 # 8GB
+ # zAPI can only handle 32 block ranges per call
+ br_limit = 32
+ z_limit = br_limit * bc_limit # 256 GB
+ z_calls = int(math.ceil(block_count / float(z_limit)))
+ zbc = block_count
+ for call in range(0, z_calls):
+ if zbc > z_limit:
+ block_count = z_limit
+ zbc -= z_limit
+ else:
+ block_count = zbc
+ clone_create = NaElement.create_node_with_children(
+ 'clone-create',
+ **{'volume': volume, 'source-path': name,
+ 'destination-path': new_name,
+ 'space-reserve': space_reserved})
+ if block_count > 0:
+ block_ranges = NaElement("block-ranges")
+ segments = int(math.ceil(block_count / float(bc_limit)))
+ bc = block_count
+ for segment in range(0, segments):
+ if bc > bc_limit:
+ block_count = bc_limit
+ bc -= bc_limit
+ else:
+ block_count = bc
+ block_range = NaElement.create_node_with_children(
+ 'block-range',
+ **{'source-block-number': str(src_block),
+ 'destination-block-number': str(dest_block),
+ 'block-count': str(block_count)})
+ block_ranges.add_child_elem(block_range)
+ src_block += int(block_count)
+ dest_block += int(block_count)
+ clone_create.add_child_elem(block_ranges)
+ self.client.invoke_successfully(clone_create, True)
LOG.debug(_("Cloned LUN with new name %s") % new_name)
lun = self._get_lun_by_args(vserver=self.vserver, path='/vol/%s/%s'
% (volume, new_name))
path = metadata['Path']
(parent, splitter, name) = path.rpartition('/')
clone_path = '%s/%s' % (parent, new_name)
- clone_start = NaElement.create_node_with_children(
- 'clone-start', **{'source-path': path,
- 'destination-path': clone_path,
- 'no-snap': 'true'})
- if block_count > 0:
- block_ranges = NaElement("block-ranges")
- # zAPI can only handle 2^24 block ranges
- bc_limit = 2 ** 24 # 8GB
- segments = int(math.ceil(float(block_count) / float(bc_limit)))
- bc = block_count
- for segment in range(0, segments):
- if bc > bc_limit:
- block_count = bc_limit
- bc -= bc_limit
- else:
- block_count = bc
- block_range = NaElement.create_node_with_children(
- 'block-range',
- **{'source-block-number': str(src_block),
- 'destination-block-number': str(dest_block),
- 'block-count': str(block_count)})
- block_ranges.add_child_elem(block_range)
- src_block = int(src_block) + int(block_count)
- dest_block = int(dest_block) + int(block_count)
- clone_start.add_child_elem(block_ranges)
- result = self.client.invoke_successfully(clone_start, True)
- clone_id_el = result.get_child_by_name('clone-id')
- cl_id_info = clone_id_el.get_child_by_name('clone-id-info')
- vol_uuid = cl_id_info.get_child_content('volume-uuid')
- clone_id = cl_id_info.get_child_content('clone-op-id')
- if vol_uuid:
- self._check_clone_status(clone_id, vol_uuid, name, new_name)
+ # zAPI can only handle 2^24 blocks per range
+ bc_limit = 2 ** 24 # 8GB
+ # zAPI can only handle 32 block ranges per call
+ br_limit = 32
+ z_limit = br_limit * bc_limit # 256 GB
+ z_calls = int(math.ceil(block_count / float(z_limit)))
+ zbc = block_count
+ for call in range(0, z_calls):
+ if zbc > z_limit:
+ block_count = z_limit
+ zbc -= z_limit
+ else:
+ block_count = zbc
+ clone_start = NaElement.create_node_with_children(
+ 'clone-start', **{'source-path': path,
+ 'destination-path': clone_path,
+ 'no-snap': 'true'})
+ if block_count > 0:
+ block_ranges = NaElement("block-ranges")
+ # zAPI can only handle 2^24 block ranges
+ bc_limit = 2 ** 24 # 8GB
+ segments = int(math.ceil(block_count / float(bc_limit)))
+ bc = block_count
+ for segment in range(0, segments):
+ if bc > bc_limit:
+ block_count = bc_limit
+ bc -= bc_limit
+ else:
+ block_count = bc
+ block_range = NaElement.create_node_with_children(
+ 'block-range',
+ **{'source-block-number': str(src_block),
+ 'destination-block-number': str(dest_block),
+ 'block-count': str(block_count)})
+ block_ranges.add_child_elem(block_range)
+ src_block += int(block_count)
+ dest_block += int(block_count)
+ clone_start.add_child_elem(block_ranges)
+ result = self.client.invoke_successfully(clone_start, True)
+ clone_id_el = result.get_child_by_name('clone-id')
+ cl_id_info = clone_id_el.get_child_by_name('clone-id-info')
+ vol_uuid = cl_id_info.get_child_content('volume-uuid')
+ clone_id = cl_id_info.get_child_content('clone-op-id')
+ if vol_uuid:
+ self._check_clone_status(clone_id, vol_uuid, name, new_name)
self.vol_refresh_voluntary = True
luns = self._get_lun_by_args(path=clone_path)
if luns: