self.driver.create_volume(self.volume)
self.driver.extend_volume(self.volume, 4)
- def test_initialize_connection_no_target_details_found(self):
- fake_volume = {'name': 'mock-vol'}
- fake_connector = {'initiator': 'iqn.mock'}
- self.driver._map_lun = mock.Mock(return_value='mocked-lun-id')
- self.driver._get_iscsi_service_details = mock.Mock(
- return_value='mocked-iqn')
- self.driver._get_target_details = mock.Mock(return_value=[])
- expected = (_('No iscsi target details were found for LUN %s')
- % fake_volume['name'])
- try:
- self.driver.initialize_connection(fake_volume, fake_connector)
- except exception.VolumeBackendAPIException as exc:
- if expected not in str(exc):
- self.fail(_('Expected exception message is missing'))
- else:
- self.fail(_('VolumeBackendAPIException not raised'))
-
class NetAppDriverNegativeTestCase(test.TestCase):
"""Test case for NetAppDriver"""
-
# Copyright (c) 2012 NetApp, Inc.
# All Rights Reserved.
#
import mox
from mox import IgnoreArg
from mox import IsA
+import six
from cinder import context
from cinder import exception
-from cinder.i18n import _
+from cinder.i18n import _LW
from cinder.image import image_utils
from cinder.openstack.common import log as logging
from cinder import test
volume = FakeVolume()
setattr(volume, 'provider_location', '127.0.0.1:/nfs')
+ drv.zapi_client = mox.CreateMockAnything()
mox.StubOutWithMock(drv, '_get_host_ip')
mox.StubOutWithMock(drv, '_get_export_path')
- mox.StubOutWithMock(drv, '_get_if_info_by_ip')
- mox.StubOutWithMock(drv, '_get_vol_by_junc_vserver')
- mox.StubOutWithMock(drv, '_clone_file')
mox.StubOutWithMock(drv, '_post_prov_deprov_in_ssc')
+ drv.zapi_client.get_if_info_by_ip('127.0.0.1').AndReturn(
+ self._prepare_info_by_ip_response())
+ drv.zapi_client.get_vol_by_junc_vserver('openstack', '/nfs').AndReturn(
+ 'nfsvol')
+ drv.zapi_client.clone_file('nfsvol', 'volume_name', 'clone_name',
+ 'openstack')
drv._get_host_ip(IgnoreArg()).AndReturn('127.0.0.1')
drv._get_export_path(IgnoreArg()).AndReturn('/nfs')
- drv._get_if_info_by_ip('127.0.0.1').AndReturn(
- self._prepare_info_by_ip_response())
- drv._get_vol_by_junc_vserver('openstack', '/nfs').AndReturn('nfsvol')
- drv._clone_file('nfsvol', 'volume_name', 'clone_name',
- 'openstack')
drv._post_prov_deprov_in_ssc(IgnoreArg())
return mox
volume_name = 'volume_name'
clone_name = 'clone_name'
- volume_id = volume_name + str(hash(volume_name))
+ volume_id = volume_name + six.text_type(hash(volume_name))
share = 'ip:/share'
drv._clone_volume(volume_name, clone_name, volume_id, share)
if (share == 'testshare' and file_name == 'img-cache-id'):
pass
else:
- LOG.warn(_("Share %(share)s and file name %(file_name)s")
- % {'share': share, 'file_name': file_name})
+ LOG.warning(_LW("Share %(share)s and file name %(file_name)s")
+ % {'share': share, 'file_name': file_name})
self.fail('Return result is unexpected')
def test_find_old_cache_files_notexists(self):
volume_info = self._driver.create_volume(FakeVolume(host, 1))
self.assertEqual(volume_info.get('provider_location'),
fake_share)
- self.assertEqual(0, utils.LOG.warn.call_count)
+ self.assertEqual(0, utils.LOG.warning.call_count)
@mock.patch.object(utils, 'LOG', mock.Mock())
@mock.patch.object(netapp_nfs, 'get_volume_extra_specs')
self._driver.create_volume(FakeVolume(host, 1))
warn_msg = 'Extra spec netapp:raid_type is obsolete. ' \
'Use netapp_raid_type instead.'
- utils.LOG.warn.assert_called_once_with(warn_msg)
+ utils.LOG.warning.assert_called_once_with(warn_msg)
@mock.patch.object(utils, 'LOG', mock.Mock())
@mock.patch.object(netapp_nfs, 'get_volume_extra_specs')
self._driver.create_volume(FakeVolume(host, 1))
warn_msg = 'Extra spec netapp_thick_provisioned is ' \
'deprecated. Use netapp_thin_provisioned instead.'
- utils.LOG.warn.assert_called_once_with(warn_msg)
+ utils.LOG.warning.assert_called_once_with(warn_msg)
def test_create_volume_no_pool_specified(self):
drv = self._driver
setattr(volume, 'provider_location', '127.0.0.1:/nfs')
mox.StubOutWithMock(drv, '_get_export_ip_path')
- mox.StubOutWithMock(drv, '_get_actual_path_for_export')
- mox.StubOutWithMock(drv, '_start_clone')
- mox.StubOutWithMock(drv, '_wait_for_clone_finish')
- if status == 'fail':
- mox.StubOutWithMock(drv, '_clear_clone')
drv._get_export_ip_path(
IgnoreArg(), IgnoreArg()).AndReturn(('127.0.0.1', '/nfs'))
- drv._get_actual_path_for_export(IgnoreArg()).AndReturn('/vol/vol1/nfs')
- drv._start_clone(IgnoreArg(), IgnoreArg()).AndReturn(('1', '2'))
- if status == 'fail':
- drv._wait_for_clone_finish('1', '2').AndRaise(
- api.NaApiError('error', 'error'))
- drv._clear_clone('1')
- else:
- drv._wait_for_clone_finish('1', '2')
return mox
def test_clone_volume_clear(self):
drv = self._driver
mox = self._prepare_clone_mock('fail')
+ drv.zapi_client = mox.CreateMockAnything()
+ drv.zapi_client.get_actual_path_for_export('/nfs').AndReturn(
+ '/vol/vol1/nfs')
+ drv.zapi_client.clone_file(IgnoreArg(), IgnoreArg())
mox.ReplayAll()
volume_name = 'volume_name'
clone_name = 'clone_name'
- volume_id = volume_name + str(hash(volume_name))
+ volume_id = volume_name + six.text_type(hash(volume_name))
try:
drv._clone_volume(volume_name, clone_name, volume_id)
except Exception as e:
configuration)
configuration.netapp_storage_family = 'ontap_7mode'
return configuration
+
+ def test_clone_volume(self):
+ drv = self._driver
+ mox = self._prepare_clone_mock('pass')
+ drv.zapi_client = mox.CreateMockAnything()
+ drv.zapi_client.get_actual_path_for_export('/nfs').AndReturn(
+ '/vol/vol1/nfs')
+ drv.zapi_client.clone_file(IgnoreArg(), IgnoreArg())
+
+ mox.ReplayAll()
+
+ volume_name = 'volume_name'
+ clone_name = 'clone_name'
+ volume_id = volume_name + six.text_type(hash(volume_name))
+ share = 'ip:/share'
+
+ drv._clone_volume(volume_name, clone_name, volume_id, share)
+
+ mox.VerifyAll()
--- /dev/null
+# Copyright (c) - 2014, Alex Meade. All rights reserved.
+# 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.
+
+import uuid
+
+from lxml import etree
+import mock
+import six
+
+from cinder import test
+from cinder.volume.drivers.netapp import api as netapp_api
+from cinder.volume.drivers.netapp.client import base
+
+
+class NetAppBaseClientTestCase(test.TestCase):
+
+ def setUp(self):
+ super(NetAppBaseClientTestCase, self).setUp()
+ self.connection = mock.MagicMock()
+ self.client = base.Client(self.connection)
+ self.fake_volume = six.text_type(uuid.uuid4())
+ self.fake_lun = six.text_type(uuid.uuid4())
+ self.fake_size = '1024'
+ self.fake_metadata = {
+ 'OsType': 'linux',
+ 'SpaceReserved': 'true',
+ }
+
+ def tearDown(self):
+ super(NetAppBaseClientTestCase, self).tearDown()
+
+ def test_get_ontapi_version(self):
+ version_response = netapp_api.NaElement(
+ etree.XML("""<results status="passed">
+ <major-version>1</major-version>
+ <minor-version>19</minor-version>
+ </results>"""))
+ self.connection.invoke_successfully.return_value = version_response
+
+ major, minor = self.client.get_ontapi_version()
+
+ self.assertEqual('1', major)
+ self.assertEqual('19', minor)
+
+ def test_create_lun(self):
+ expected_path = '/vol/%s/%s' % (self.fake_volume, self.fake_lun)
+
+ with mock.patch.object(netapp_api.NaElement,
+ 'create_node_with_children',
+ ) as mock_create_node:
+ self.client.create_lun(self.fake_volume,
+ self.fake_lun,
+ self.fake_size,
+ self.fake_metadata)
+
+ mock_create_node.assert_called_once_with(
+ 'lun-create-by-size',
+ **{'path': expected_path,
+ 'size': self.fake_size,
+ 'ostype': self.fake_metadata['OsType'],
+ 'space-reservation-enabled':
+ self.fake_metadata['SpaceReserved']})
+ self.connection.invoke_successfully.assert_called_once_with(
+ mock.ANY, True)
+
+ def test_create_lun_with_qos_policy_group(self):
+ expected_path = '/vol/%s/%s' % (self.fake_volume, self.fake_lun)
+ expected_qos_group = 'qos_1'
+ mock_request = mock.Mock()
+
+ with mock.patch.object(netapp_api.NaElement,
+ 'create_node_with_children',
+ return_value=mock_request
+ ) as mock_create_node:
+ self.client.create_lun(self.fake_volume,
+ self.fake_lun,
+ self.fake_size,
+ self.fake_metadata,
+ qos_policy_group=expected_qos_group)
+
+ mock_create_node.assert_called_once_with(
+ 'lun-create-by-size',
+ **{'path': expected_path, 'size': self.fake_size,
+ 'ostype': self.fake_metadata['OsType'],
+ 'space-reservation-enabled':
+ self.fake_metadata['SpaceReserved']})
+ mock_request.add_new_child.assert_called_once_with(
+ 'qos-policy-group', expected_qos_group)
+ self.connection.invoke_successfully.assert_called_once_with(
+ mock.ANY, True)
+
+ def test_create_lun_raises_on_failure(self):
+ self.connection.invoke_successfully = mock.Mock(
+ side_effect=netapp_api.NaApiError)
+ self.assertRaises(netapp_api.NaApiError,
+ self.client.create_lun,
+ self.fake_volume,
+ self.fake_lun,
+ self.fake_size,
+ self.fake_metadata)
+
+ def test_destroy_lun(self):
+ path = '/vol/%s/%s' % (self.fake_volume, self.fake_lun)
+
+ with mock.patch.object(netapp_api.NaElement,
+ 'create_node_with_children',
+ ) as mock_create_node:
+ self.client.destroy_lun(path)
+
+ mock_create_node.assert_called_once_with(
+ 'lun-destroy',
+ **{'path': path})
+ self.connection.invoke_successfully.assert_called_once_with(
+ mock.ANY, True)
+
+ def test_destroy_lun_force(self):
+ path = '/vol/%s/%s' % (self.fake_volume, self.fake_lun)
+ mock_request = mock.Mock()
+
+ with mock.patch.object(netapp_api.NaElement,
+ 'create_node_with_children',
+ return_value=mock_request
+ ) as mock_create_node:
+ self.client.destroy_lun(path)
+
+ mock_create_node.assert_called_once_with('lun-destroy',
+ **{'path': path})
+ mock_request.add_new_child.assert_called_once_with('force', 'true')
+ self.connection.invoke_successfully.assert_called_once_with(
+ mock.ANY, True)
+
+ def test_map_lun(self):
+ path = '/vol/%s/%s' % (self.fake_volume, self.fake_lun)
+ igroup = 'igroup'
+ expected_lun_id = 'my_lun'
+ mock_response = mock.Mock()
+ self.connection.invoke_successfully.return_value = mock_response
+ mock_response.get_child_content.return_value = expected_lun_id
+
+ with mock.patch.object(netapp_api.NaElement,
+ 'create_node_with_children',
+ ) as mock_create_node:
+ actual_lun_id = self.client.map_lun(path, igroup)
+
+ mock_create_node.assert_called_once_with(
+ 'lun-map',
+ **{'path': path, 'initiator-group': igroup})
+ self.connection.invoke_successfully.assert_called_once_with(
+ mock.ANY, True)
+ self.assertEqual(expected_lun_id, actual_lun_id)
+
+ def test_map_lun_with_lun_id(self):
+ path = '/vol/%s/%s' % (self.fake_volume, self.fake_lun)
+ igroup = 'igroup'
+ expected_lun_id = 'my_lun'
+ mock_response = mock.Mock()
+ self.connection.invoke_successfully.return_value = mock_response
+ mock_response.get_child_content.return_value = expected_lun_id
+
+ with mock.patch.object(netapp_api.NaElement,
+ 'create_node_with_children',
+ ) as mock_create_node:
+ actual_lun_id = self.client.map_lun(path, igroup,
+ lun_id=expected_lun_id)
+
+ mock_create_node.assert_called_once_with(
+ 'lun-map',
+ **{'path': path, 'initiator-group': igroup})
+ self.connection.invoke_successfully.assert_called_once_with(
+ mock.ANY, True)
+ self.assertEqual(expected_lun_id, actual_lun_id)
+
+ def test_map_lun_with_api_error(self):
+ path = '/vol/%s/%s' % (self.fake_volume, self.fake_lun)
+ igroup = 'igroup'
+ self.connection.invoke_successfully.side_effect =\
+ netapp_api.NaApiError()
+
+ with mock.patch.object(netapp_api.NaElement,
+ 'create_node_with_children',
+ ) as mock_create_node:
+ self.assertRaises(netapp_api.NaApiError, self.client.map_lun,
+ path, igroup)
+
+ mock_create_node.assert_called_once_with(
+ 'lun-map',
+ **{'path': path, 'initiator-group': igroup})
+ self.connection.invoke_successfully.assert_called_once_with(
+ mock.ANY, True)
+
+ def test_unmap_lun(self):
+ path = '/vol/%s/%s' % (self.fake_volume, self.fake_lun)
+ igroup = 'igroup'
+ mock_response = mock.Mock()
+ self.connection.invoke_successfully.return_value = mock_response
+
+ with mock.patch.object(netapp_api.NaElement,
+ 'create_node_with_children',
+ ) as mock_create_node:
+ self.client.unmap_lun(path, igroup)
+
+ mock_create_node.assert_called_once_with(
+ 'lun-unmap',
+ **{'path': path, 'initiator-group': igroup})
+ self.connection.invoke_successfully.assert_called_once_with(
+ mock.ANY, True)
+
+ def test_unmap_lun_with_api_error(self):
+ path = '/vol/%s/%s' % (self.fake_volume, self.fake_lun)
+ igroup = 'igroup'
+ self.connection.invoke_successfully.side_effect =\
+ netapp_api.NaApiError()
+
+ with mock.patch.object(netapp_api.NaElement,
+ 'create_node_with_children',
+ ) as mock_create_node:
+ self.assertRaises(netapp_api.NaApiError, self.client.unmap_lun,
+ path, igroup)
+
+ mock_create_node.assert_called_once_with(
+ 'lun-unmap',
+ **{'path': path, 'initiator-group': igroup})
+
+ def test_unmap_lun_already_unmapped(self):
+ path = '/vol/%s/%s' % (self.fake_volume, self.fake_lun)
+ igroup = 'igroup'
+ EINVALIDINPUTERROR = '13115'
+ self.connection.invoke_successfully.side_effect =\
+ netapp_api.NaApiError(code=EINVALIDINPUTERROR)
+
+ with mock.patch.object(netapp_api.NaElement,
+ 'create_node_with_children',
+ ) as mock_create_node:
+ self.client.unmap_lun(path, igroup)
+
+ mock_create_node.assert_called_once_with(
+ 'lun-unmap',
+ **{'path': path, 'initiator-group': igroup})
+ self.connection.invoke_successfully.assert_called_once_with(
+ mock.ANY, True)
+
+ def test_unmap_lun_lun_not_mapped_in_group(self):
+ path = '/vol/%s/%s' % (self.fake_volume, self.fake_lun)
+ igroup = 'igroup'
+ EVDISK_ERROR_NO_SUCH_LUNMAP = '9016'
+ self.connection.invoke_successfully.side_effect =\
+ netapp_api.NaApiError(code=EVDISK_ERROR_NO_SUCH_LUNMAP)
+
+ with mock.patch.object(netapp_api.NaElement,
+ 'create_node_with_children',
+ ) as mock_create_node:
+ self.client.unmap_lun(path, igroup)
+
+ mock_create_node.assert_called_once_with(
+ 'lun-unmap',
+ **{'path': path, 'initiator-group': igroup})
+ self.connection.invoke_successfully.assert_called_once_with(
+ mock.ANY, True)
+
+ def test_create_igroup(self):
+ igroup = 'igroup'
+
+ with mock.patch.object(netapp_api.NaElement,
+ 'create_node_with_children',
+ ) as mock_create_node:
+ self.client.create_igroup(igroup)
+
+ mock_create_node.assert_called_once_with(
+ 'igroup-create',
+ **{'initiator-group-name': igroup,
+ 'initiator-group-type': 'iscsi',
+ 'os-type': 'default'})
+ self.connection.invoke_successfully.assert_called_once_with(
+ mock.ANY, True)
+
+ def test_add_igroup_initiator(self):
+ igroup = 'igroup'
+ initiator = 'initiator'
+
+ with mock.patch.object(netapp_api.NaElement,
+ 'create_node_with_children',
+ ) as mock_create_node:
+ self.client.add_igroup_initiator(igroup, initiator)
+
+ mock_create_node.assert_called_once_with(
+ 'igroup-add',
+ **{'initiator-group-name': igroup,
+ 'initiator': initiator})
+ self.connection.invoke_successfully.assert_called_once_with(
+ mock.ANY, True)
+
+ def test_do_direct_resize(self):
+ path = '/vol/%s/%s' % (self.fake_volume, self.fake_lun)
+ new_size = 1024
+ mock_request = mock.Mock()
+
+ with mock.patch.object(netapp_api.NaElement,
+ 'create_node_with_children',
+ return_value=mock_request
+ ) as mock_create_node:
+ self.client.do_direct_resize(path, new_size)
+
+ mock_create_node.assert_called_once_with(
+ 'lun-resize',
+ **{'path': path,
+ 'size': new_size})
+ mock_request.add_new_child.assert_called_once_with(
+ 'force', 'true')
+ self.connection.invoke_successfully.assert_called_once_with(
+ mock.ANY, True)
+
+ def test_do_direct_resize_not_forced(self):
+ path = '/vol/%s/%s' % (self.fake_volume, self.fake_lun)
+ new_size = 1024
+ mock_request = mock.Mock()
+
+ with mock.patch.object(netapp_api.NaElement,
+ 'create_node_with_children',
+ return_value=mock_request
+ ) as mock_create_node:
+ self.client.do_direct_resize(path, new_size, force=False)
+
+ mock_create_node.assert_called_once_with(
+ 'lun-resize',
+ **{'path': path,
+ 'size': new_size})
+ self.assertFalse(mock_request.add_new_child.called)
+ self.connection.invoke_successfully.assert_called_once_with(
+ mock.ANY, True)
+
+ def test_get_lun_geometry(self):
+ expected_keys = set(['size', 'bytes_per_sector', 'sectors_per_track',
+ 'tracks_per_cylinder', 'cylinders', 'max_resize'])
+ path = '/vol/%s/%s' % (self.fake_volume, self.fake_lun)
+ mock_response = mock.Mock()
+ self.connection.invoke_successfully.return_value = mock_response
+
+ geometry = self.client.get_lun_geometry(path)
+ self.assertEqual(expected_keys, set(geometry.keys()))
+
+ def test_get_lun_geometry_with_api_error(self):
+ path = '/vol/%s/%s' % (self.fake_volume, self.fake_lun)
+ self.connection.invoke_successfully.side_effect =\
+ netapp_api.NaApiError()
+ geometry = self.client.get_lun_geometry(path)
+
+ self.assertEqual({}, geometry)
+
+ def test_get_volume_options(self):
+ fake_response = netapp_api.NaElement('volume')
+ fake_response.add_node_with_children('options', test='blah')
+ self.connection.invoke_successfully.return_value = fake_response
+ options = self.client.get_volume_options('volume')
+
+ self.assertEqual(1, len(options))
+
+ def test_get_volume_options_with_no_options(self):
+ fake_response = netapp_api.NaElement('options')
+ self.connection.invoke_successfully.return_value = fake_response
+ options = self.client.get_volume_options('volume')
+
+ self.assertEqual([], options)
+
+ def test_move_lun(self):
+ path = '/vol/%s/%s' % (self.fake_volume, self.fake_lun)
+ new_path = '/vol/%s/%s' % (self.fake_volume, self.fake_lun)
+ fake_response = netapp_api.NaElement('options')
+ self.connection.invoke_successfully.return_value = fake_response
+ self.client.move_lun(path, new_path)
+
+ self.connection.invoke_successfully.assert_called_once_with(
+ mock.ANY, True)
--- /dev/null
+# Copyright (c) - 2014, Alex Meade. All rights reserved.
+# 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.
+
+import uuid
+
+from lxml import etree
+import mock
+import six
+
+from cinder import exception
+from cinder import test
+from cinder.volume.drivers.netapp import api as netapp_api
+from cinder.volume.drivers.netapp.client import cmode
+
+
+class NetAppCmodeClientTestCase(test.TestCase):
+
+ def setUp(self):
+ super(NetAppCmodeClientTestCase, self).setUp()
+ self.connection = mock.MagicMock()
+ self.vserver = 'fake_vserver'
+ self.client = cmode.Client(self.connection, self.vserver)
+ self.fake_volume = six.text_type(uuid.uuid4())
+ self.fake_lun = six.text_type(uuid.uuid4())
+
+ def tearDown(self):
+ super(NetAppCmodeClientTestCase, self).tearDown()
+
+ def test_get_target_details_no_targets(self):
+ response = netapp_api.NaElement(
+ etree.XML("""<results status="passed">
+ <num-records>1</num-records>
+ <attributes-list></attributes-list>
+ </results>"""))
+ self.connection.invoke_successfully.return_value = response
+ target_list = self.client.get_target_details()
+
+ self.assertEqual([], target_list)
+
+ def test_get_target_details(self):
+ expected_target = {
+ "address": "127.0.0.1",
+ "port": "1337",
+ "interface-enabled": "true",
+ "tpgroup-tag": "7777",
+ }
+ response = netapp_api.NaElement(
+ etree.XML("""<results status="passed">
+ <num-records>1</num-records>
+ <attributes-list>
+ <iscsi-interface-list-entry-info>
+ <ip-address>%(address)s</ip-address>
+ <ip-port>%(port)s</ip-port>
+ <is-interface-enabled>%(interface-enabled)s</is-interface-enabled>
+ <tpgroup-tag>%(tpgroup-tag)s</tpgroup-tag>
+ </iscsi-interface-list-entry-info>
+ </attributes-list>
+ </results>""" % expected_target))
+ self.connection.invoke_successfully.return_value = response
+
+ target_list = self.client.get_target_details()
+
+ self.assertEqual([expected_target], target_list)
+
+ def test_get_iscsi_service_details_with_no_iscsi_service(self):
+ response = netapp_api.NaElement(
+ etree.XML("""<results status="passed">
+ <num-records>0</num-records>
+ </results>"""))
+ self.connection.invoke_successfully.return_value = response
+
+ iqn = self.client.get_iscsi_service_details()
+
+ self.assertEqual(None, iqn)
+
+ def test_get_iscsi_service_details(self):
+ expected_iqn = 'iqn.1998-01.org.openstack.iscsi:name1'
+ response = netapp_api.NaElement(
+ etree.XML("""<results status="passed">
+ <num-records>1</num-records>
+ <attributes-list>
+ <iscsi-service-info>
+ <node-name>%s</node-name>
+ </iscsi-service-info>
+ </attributes-list>
+ </results>""" % expected_iqn))
+ self.connection.invoke_successfully.return_value = response
+
+ iqn = self.client.get_iscsi_service_details()
+
+ self.assertEqual(expected_iqn, iqn)
+
+ def test_get_lun_list(self):
+ response = netapp_api.NaElement(
+ etree.XML("""<results status="passed">
+ <num-records>2</num-records>
+ <attributes-list>
+ <lun-info>
+ </lun-info>
+ <lun-info>
+ </lun-info>
+ </attributes-list>
+ </results>"""))
+ self.connection.invoke_successfully.return_value = response
+
+ luns = self.client.get_lun_list()
+
+ self.assertEqual(2, len(luns))
+
+ def test_get_lun_list_with_multiple_pages(self):
+ response = netapp_api.NaElement(
+ etree.XML("""<results status="passed">
+ <num-records>2</num-records>
+ <attributes-list>
+ <lun-info> </lun-info>
+ <lun-info> </lun-info>
+ </attributes-list>
+ <next-tag>fake-next</next-tag>
+ </results>"""))
+ response_2 = netapp_api.NaElement(
+ etree.XML("""<results status="passed">
+ <num-records>2</num-records>
+ <attributes-list>
+ <lun-info> </lun-info>
+ <lun-info> </lun-info>
+ </attributes-list>
+ </results>"""))
+ self.connection.invoke_successfully.side_effect = [response,
+ response_2]
+
+ luns = self.client.get_lun_list()
+
+ self.assertEqual(4, len(luns))
+
+ def test_get_lun_map_no_luns_mapped(self):
+ path = '/vol/%s/%s' % (self.fake_volume, self.fake_lun)
+ response = netapp_api.NaElement(
+ etree.XML("""<results status="passed">
+ <num-records>0</num-records>
+ <attributes-list></attributes-list>
+ </results>"""))
+ self.connection.invoke_successfully.return_value = response
+
+ lun_map = self.client.get_lun_map(path)
+
+ self.assertEqual([], lun_map)
+
+ def test_get_lun_map(self):
+ path = '/vol/%s/%s' % (self.fake_volume, self.fake_lun)
+ expected_lun_map = {
+ "initiator-group": "igroup",
+ "lun-id": "1337",
+ "vserver": "vserver",
+ }
+ response = netapp_api.NaElement(
+ etree.XML("""<results status="passed">
+ <num-records>1</num-records>
+ <attributes-list>
+ <lun-map-info>
+ <lun-id>%(lun-id)s</lun-id>
+ <initiator-group>%(initiator-group)s</initiator-group>
+ <vserver>%(vserver)s</vserver>
+ </lun-map-info>
+ </attributes-list>
+ </results>""" % expected_lun_map))
+ self.connection.invoke_successfully.return_value = response
+
+ lun_map = self.client.get_lun_map(path)
+
+ self.assertEqual([expected_lun_map], lun_map)
+
+ def test_get_lun_map_multiple_pages(self):
+ path = '/vol/%s/%s' % (self.fake_volume, self.fake_lun)
+ expected_lun_map = {
+ "initiator-group": "igroup",
+ "lun-id": "1337",
+ "vserver": "vserver",
+ }
+ response = netapp_api.NaElement(
+ etree.XML("""<results status="passed">
+ <num-records>1</num-records>
+ <attributes-list>
+ <lun-map-info>
+ <lun-id>%(lun-id)s</lun-id>
+ <initiator-group>%(initiator-group)s</initiator-group>
+ <vserver>%(vserver)s</vserver>
+ </lun-map-info>
+ </attributes-list>
+ <next-tag>blah</next-tag>
+ </results>""" % expected_lun_map))
+ response_2 = netapp_api.NaElement(
+ etree.XML("""<results status="passed">
+ <num-records>1</num-records>
+ <attributes-list>
+ <lun-map-info>
+ <lun-id>%(lun-id)s</lun-id>
+ <initiator-group>%(initiator-group)s</initiator-group>
+ <vserver>%(vserver)s</vserver>
+ </lun-map-info>
+ </attributes-list>
+ </results>""" % expected_lun_map))
+ self.connection.invoke_successfully.side_effect = [response,
+ response_2]
+
+ lun_map = self.client.get_lun_map(path)
+
+ self.assertEqual([expected_lun_map, expected_lun_map], lun_map)
+
+ def test_get_igroup_by_initiator_none_found(self):
+ initiator = 'initiator'
+ response = netapp_api.NaElement(
+ etree.XML("""<results status="passed">
+ <num-records>0</num-records>
+ <attributes-list></attributes-list>
+ </results>"""))
+ self.connection.invoke_successfully.return_value = response
+
+ igroup = self.client.get_igroup_by_initiator(initiator)
+
+ self.assertEqual([], igroup)
+
+ def test_get_igroup_by_initiator(self):
+ initiator = 'initiator'
+ expected_igroup = {
+ "initiator-group-os-type": None,
+ "initiator-group-type": "1337",
+ "initiator-group-name": "vserver",
+ }
+ response = netapp_api.NaElement(
+ etree.XML("""<results status="passed">
+ <num-records>1</num-records>
+ <attributes-list>
+ <initiator-group-info>
+ <initiator-group-type>%(initiator-group-type)s</initiator-group-type>
+ <initiator-group-name>%(initiator-group-name)s</initiator-group-name>
+ </initiator-group-info>
+ </attributes-list>
+ </results>""" % expected_igroup))
+ self.connection.invoke_successfully.return_value = response
+
+ igroup = self.client.get_igroup_by_initiator(initiator)
+
+ self.assertEqual([expected_igroup], igroup)
+
+ def test_get_igroup_by_initiator_multiple_pages(self):
+ initiator = 'initiator'
+ expected_igroup = {
+ "initiator-group-os-type": None,
+ "initiator-group-type": "1337",
+ "initiator-group-name": "vserver",
+ }
+ response = netapp_api.NaElement(
+ etree.XML("""<results status="passed">
+ <num-records>1</num-records>
+ <attributes-list>
+ <initiator-group-info>
+ <initiator-group-type>%(initiator-group-type)s</initiator-group-type>
+ <initiator-group-name>%(initiator-group-name)s</initiator-group-name>
+ </initiator-group-info>
+ </attributes-list>
+ <next-tag>blah</next-tag>
+ </results>""" % expected_igroup))
+ response_2 = netapp_api.NaElement(
+ etree.XML("""<results status="passed">
+ <num-records>1</num-records>
+ <attributes-list>
+ <initiator-group-info>
+ <initiator-group-type>%(initiator-group-type)s</initiator-group-type>
+ <initiator-group-name>%(initiator-group-name)s</initiator-group-name>
+ </initiator-group-info>
+ </attributes-list>
+ </results>""" % expected_igroup))
+ self.connection.invoke_successfully.side_effect = [response,
+ response_2]
+
+ igroup = self.client.get_igroup_by_initiator(initiator)
+
+ self.assertEqual([expected_igroup, expected_igroup], igroup)
+
+ def test_clone_lun(self):
+ self.client.clone_lun('volume', 'fakeLUN', 'newFakeLUN')
+ self.assertEqual(1, self.connection.invoke_successfully.call_count)
+
+ 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.client.clone_lun('volume', 'fakeLUN', 'newFakeLUN',
+ block_count=bc)
+ self.assertEqual(2, self.connection.invoke_successfully.call_count)
+
+ def test_get_lun_by_args(self):
+ response = netapp_api.NaElement(
+ etree.XML("""<results status="passed">
+ <num-records>2</num-records>
+ <attributes-list>
+ <lun-info>
+ </lun-info>
+ </attributes-list>
+ </results>"""))
+ self.connection.invoke_successfully.return_value = response
+
+ lun = self.client.get_lun_by_args()
+
+ self.assertEqual(1, len(lun))
+
+ def test_get_lun_by_args_no_lun_found(self):
+ response = netapp_api.NaElement(
+ etree.XML("""<results status="passed">
+ <num-records>2</num-records>
+ <attributes-list>
+ </attributes-list>
+ </results>"""))
+ self.connection.invoke_successfully.return_value = response
+
+ lun = self.client.get_lun_by_args()
+
+ self.assertEqual(0, len(lun))
+
+ def test_get_lun_by_args_with_args_specified(self):
+ path = '/vol/%s/%s' % (self.fake_volume, self.fake_lun)
+ response = netapp_api.NaElement(
+ etree.XML("""<results status="passed">
+ <num-records>2</num-records>
+ <attributes-list>
+ <lun-info>
+ </lun-info>
+ </attributes-list>
+ </results>"""))
+ self.connection.invoke_successfully.return_value = response
+
+ lun = self.client.get_lun_by_args(path=path)
+
+ __, _args, __ = self.connection.invoke_successfully.mock_calls[0]
+ actual_request = _args[0]
+ query = actual_request.get_child_by_name('query')
+ lun_info_args = query.get_child_by_name('lun-info').get_children()
+
+ # Assert request is made with correct arguments
+ self.assertEqual('path', lun_info_args[0].get_name())
+ self.assertEqual(path, lun_info_args[0].get_content())
+
+ self.assertEqual(1, len(lun))
+
+ def test_file_assign_qos(self):
+ expected_flex_vol = "fake_flex_vol"
+ expected_policy_group = "fake_policy_group"
+ expected_file_path = "fake_file_path"
+
+ self.client.file_assign_qos(expected_flex_vol, expected_policy_group,
+ expected_file_path)
+
+ __, _args, __ = self.connection.invoke_successfully.mock_calls[0]
+ actual_request = _args[0]
+ actual_flex_vol = actual_request.get_child_by_name('volume') \
+ .get_content()
+ actual_policy_group = actual_request \
+ .get_child_by_name('qos-policy-group-name').get_content()
+ actual_file_path = actual_request.get_child_by_name('file') \
+ .get_content()
+ actual_vserver = actual_request.get_child_by_name('vserver') \
+ .get_content()
+
+ self.assertEqual(expected_flex_vol, actual_flex_vol)
+ self.assertEqual(expected_policy_group, actual_policy_group)
+ self.assertEqual(expected_file_path, actual_file_path)
+ self.assertEqual(self.vserver, actual_vserver)
+
+ @mock.patch('cinder.volume.drivers.netapp.utils.resolve_hostname',
+ return_value='192.168.1.101')
+ def test_get_if_info_by_ip_not_found(self, mock_resolve_hostname):
+ fake_ip = '192.168.1.101'
+ response = netapp_api.NaElement(
+ etree.XML("""<results status="passed">
+ <num-records>0</num-records>
+ <attributes-list>
+ </attributes-list>
+ </results>"""))
+ self.connection.invoke_successfully.return_value = response
+
+ self.assertRaises(exception.NotFound, self.client.get_if_info_by_ip,
+ fake_ip)
+
+ @mock.patch('cinder.volume.drivers.netapp.utils.resolve_hostname',
+ return_value='192.168.1.101')
+ def test_get_if_info_by_ip(self, mock_resolve_hostname):
+ fake_ip = '192.168.1.101'
+ response = netapp_api.NaElement(
+ etree.XML("""<results status="passed">
+ <num-records>1</num-records>
+ <attributes-list>
+ <net-interface-info>
+ </net-interface-info>
+ </attributes-list>
+ </results>"""))
+ self.connection.invoke_successfully.return_value = response
+
+ results = self.client.get_if_info_by_ip(fake_ip)
+
+ self.assertEqual(1, len(results))
+
+ def test_get_vol_by_junc_vserver_not_found(self):
+ fake_vserver = 'fake_vserver'
+ fake_junc = 'fake_junction_path'
+ response = netapp_api.NaElement(
+ etree.XML("""<results status="passed">
+ <num-records>0</num-records>
+ <attributes-list>
+ </attributes-list>
+ </results>"""))
+ self.connection.invoke_successfully.return_value = response
+
+ self.assertRaises(exception.NotFound,
+ self.client.get_vol_by_junc_vserver,
+ fake_vserver, fake_junc)
+
+ def test_get_vol_by_junc_vserver(self):
+ fake_vserver = 'fake_vserver'
+ fake_junc = 'fake_junction_path'
+ expected_flex_vol = 'fake_flex_vol'
+ response = netapp_api.NaElement(
+ etree.XML("""<results status="passed">
+ <num-records>1</num-records>
+ <attributes-list>
+ <volume-attributes>
+ <volume-id-attributes>
+ <name>%(flex_vol)s</name>
+ </volume-id-attributes>
+ </volume-attributes>
+ </attributes-list>
+ </results>""" % {'flex_vol': expected_flex_vol}))
+ self.connection.invoke_successfully.return_value = response
+
+ actual_flex_vol = self.client.get_vol_by_junc_vserver(fake_vserver,
+ fake_junc)
+
+ self.assertEqual(expected_flex_vol, actual_flex_vol)
+
+ def test_clone_file(self):
+ expected_flex_vol = "fake_flex_vol"
+ expected_src_path = "fake_src_path"
+ expected_dest_path = "fake_dest_path"
+ self.connection.get_api_version.return_value = (1, 20)
+
+ self.client.clone_file(expected_flex_vol, expected_src_path,
+ expected_dest_path, self.vserver)
+
+ __, _args, __ = self.connection.invoke_successfully.mock_calls[0]
+ actual_request = _args[0]
+ actual_flex_vol = actual_request.get_child_by_name('volume') \
+ .get_content()
+ actual_src_path = actual_request \
+ .get_child_by_name('source-path').get_content()
+ actual_dest_path = actual_request.get_child_by_name(
+ 'destination-path').get_content()
+
+ self.assertEqual(expected_flex_vol, actual_flex_vol)
+ self.assertEqual(expected_src_path, actual_src_path)
+ self.assertEqual(expected_dest_path, actual_dest_path)
+ self.assertEqual(actual_request.get_child_by_name(
+ 'destination-exists'), None)
+
+ def test_clone_file_when_destination_exists(self):
+ expected_flex_vol = "fake_flex_vol"
+ expected_src_path = "fake_src_path"
+ expected_dest_path = "fake_dest_path"
+ self.connection.get_api_version.return_value = (1, 20)
+
+ self.client.clone_file(expected_flex_vol, expected_src_path,
+ expected_dest_path, self.vserver,
+ dest_exists=True)
+
+ __, _args, __ = self.connection.invoke_successfully.mock_calls[0]
+ actual_request = _args[0]
+ actual_flex_vol = actual_request.get_child_by_name('volume') \
+ .get_content()
+ actual_src_path = actual_request \
+ .get_child_by_name('source-path').get_content()
+ actual_dest_path = actual_request.get_child_by_name(
+ 'destination-path').get_content()
+
+ self.assertEqual(expected_flex_vol, actual_flex_vol)
+ self.assertEqual(expected_src_path, actual_src_path)
+ self.assertEqual(expected_dest_path, actual_dest_path)
+ self.assertEqual(actual_request.get_child_by_name(
+ 'destination-exists').get_content(), 'true')
+
+ def test_clone_file_when_destination_exists_and_version_less_than_1_20(
+ self):
+ expected_flex_vol = "fake_flex_vol"
+ expected_src_path = "fake_src_path"
+ expected_dest_path = "fake_dest_path"
+ self.connection.get_api_version.return_value = (1, 19)
+
+ self.client.clone_file(expected_flex_vol, expected_src_path,
+ expected_dest_path, self.vserver,
+ dest_exists=True)
+
+ __, _args, __ = self.connection.invoke_successfully.mock_calls[0]
+ actual_request = _args[0]
+ actual_flex_vol = actual_request.get_child_by_name('volume') \
+ .get_content()
+ actual_src_path = actual_request \
+ .get_child_by_name('source-path').get_content()
+ actual_dest_path = actual_request.get_child_by_name(
+ 'destination-path').get_content()
+
+ self.assertEqual(expected_flex_vol, actual_flex_vol)
+ self.assertEqual(expected_src_path, actual_src_path)
+ self.assertEqual(expected_dest_path, actual_dest_path)
+ self.assertEqual(actual_request.get_child_by_name(
+ 'destination-exists'), None)
+
+ def test_get_file_usage(self):
+ expected_bytes = "2048"
+ fake_vserver = 'fake_vserver'
+ fake_path = 'fake_path'
+ response = netapp_api.NaElement(
+ etree.XML("""<results status="passed">
+ <unique-bytes>%(unique-bytes)s</unique-bytes>
+ </results>""" % {'unique-bytes': expected_bytes}))
+ self.connection.invoke_successfully.return_value = response
+
+ actual_bytes = self.client.get_file_usage(fake_vserver, fake_path)
+
+ self.assertEqual(expected_bytes, actual_bytes)
--- /dev/null
+# Copyright (c) - 2014, Alex Meade. All rights reserved.
+# 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.
+
+import uuid
+
+from lxml import etree
+import mock
+import six
+
+from cinder import test
+from cinder.volume.drivers.netapp import api as netapp_api
+from cinder.volume.drivers.netapp.client import seven_mode
+
+
+class NetApp7modeClientTestCase(test.TestCase):
+
+ def setUp(self):
+ super(NetApp7modeClientTestCase, self).setUp()
+ self.connection = mock.MagicMock()
+ self.fake_volume = six.text_type(uuid.uuid4())
+ self.client = seven_mode.Client(self.connection, [self.fake_volume])
+ self.fake_lun = six.text_type(uuid.uuid4())
+
+ def tearDown(self):
+ super(NetApp7modeClientTestCase, self).tearDown()
+
+ def test_get_target_details_no_targets(self):
+ response = netapp_api.NaElement(
+ etree.XML("""<results status="passed">
+ <iscsi-portal-list-entries>
+ </iscsi-portal-list-entries>
+ </results>"""))
+ self.connection.invoke_successfully.return_value = response
+
+ target_list = self.client.get_target_details()
+
+ self.assertEqual([], target_list)
+
+ def test_get_target_details(self):
+ expected_target = {
+ "address": "127.0.0.1",
+ "port": "1337",
+ "tpgroup-tag": "7777",
+ }
+ response = netapp_api.NaElement(
+ etree.XML("""<results status="passed">
+ <iscsi-portal-list-entries>
+ <iscsi-portal-list-entry-info>
+ <ip-address>%(address)s</ip-address>
+ <ip-port>%(port)s</ip-port>
+ <tpgroup-tag>%(tpgroup-tag)s</tpgroup-tag>
+ </iscsi-portal-list-entry-info>
+ </iscsi-portal-list-entries>
+ </results>""" % expected_target))
+ self.connection.invoke_successfully.return_value = response
+
+ target_list = self.client.get_target_details()
+
+ self.assertEqual([expected_target], target_list)
+
+ def test_get_iscsi_service_details_with_no_iscsi_service(self):
+ response = netapp_api.NaElement(
+ etree.XML("""<results status="passed">
+ </results>"""))
+ self.connection.invoke_successfully.return_value = response
+
+ iqn = self.client.get_iscsi_service_details()
+
+ self.assertEqual(None, iqn)
+
+ def test_get_iscsi_service_details(self):
+ expected_iqn = 'iqn.1998-01.org.openstack.iscsi:name1'
+ response = netapp_api.NaElement(
+ etree.XML("""<results status="passed">
+ <node-name>%s</node-name>
+ </results>""" % expected_iqn))
+ self.connection.invoke_successfully.return_value = response
+
+ iqn = self.client.get_iscsi_service_details()
+
+ self.assertEqual(expected_iqn, iqn)
+
+ def test_get_lun_list(self):
+ response = netapp_api.NaElement(
+ etree.XML("""<results status="passed">
+ <luns>
+ <lun-info></lun-info>
+ <lun-info></lun-info>
+ </luns>
+ </results>"""))
+ self.connection.invoke_successfully.return_value = response
+
+ luns = self.client.get_lun_list()
+
+ self.assertEqual(2, len(luns))
+
+ def test_get_igroup_by_initiator_none_found(self):
+ initiator = 'initiator'
+ response = netapp_api.NaElement(
+ etree.XML("""<results status="passed">
+ <initiator-groups>
+ </initiator-groups>
+ </results>"""))
+ self.connection.invoke_successfully.return_value = response
+
+ igroup = self.client.get_igroup_by_initiator(initiator)
+
+ self.assertEqual([], igroup)
+
+ def test_get_igroup_by_initiator(self):
+ initiator = 'initiator'
+ expected_igroup = {
+ "initiator-group-os-type": None,
+ "initiator-group-type": "1337",
+ "initiator-group-name": "vserver",
+ }
+ response = netapp_api.NaElement(
+ etree.XML("""<results status="passed">
+ <initiator-groups>
+ <initiator-group-info>
+ <initiators>
+ <initiator-info>
+ <initiator-name>initiator</initiator-name>
+ </initiator-info>
+ </initiators>
+ <initiator-group-type>%(initiator-group-type)s</initiator-group-type>
+ <initiator-group-name>%(initiator-group-name)s</initiator-group-name>
+ </initiator-group-info>
+ </initiator-groups>
+ </results>""" % expected_igroup))
+ self.connection.invoke_successfully.return_value = response
+
+ igroup = self.client.get_igroup_by_initiator(initiator)
+
+ self.assertEqual([expected_igroup], igroup)
+
+ def test_clone_lun(self):
+ fake_clone_start = netapp_api.NaElement(
+ etree.XML("""<results status="passed">
+ <clone-id>
+ <clone-id-info>
+ <clone-op-id>1337</clone-op-id>
+ <volume-uuid>volume-uuid</volume-uuid>
+ </clone-id-info>
+ </clone-id>
+ </results>"""))
+ fake_clone_status = netapp_api.NaElement(
+ etree.XML("""<results status="passed">
+ <status>
+ <ops-info>
+ <clone-state>completed</clone-state>
+ </ops-info>
+ </status>
+ </results>"""))
+
+ self.connection.invoke_successfully.side_effect = [fake_clone_start,
+ fake_clone_status]
+
+ self.client.clone_lun('path', 'new_path', 'fakeLUN', 'newFakeLUN')
+ self.assertEqual(2, self.connection.invoke_successfully.call_count)
+
+ def test_clone_lun_api_error(self):
+ fake_clone_start = netapp_api.NaElement(
+ etree.XML("""<results status="passed">
+ <clone-id>
+ <clone-id-info>
+ <clone-op-id>1337</clone-op-id>
+ <volume-uuid>volume-uuid</volume-uuid>
+ </clone-id-info>
+ </clone-id>
+ </results>"""))
+ fake_clone_status = netapp_api.NaElement(
+ etree.XML("""<results status="passed">
+ <status>
+ <ops-info>
+ <clone-state>error</clone-state>
+ </ops-info>
+ </status>
+ </results>"""))
+
+ self.connection.invoke_successfully.side_effect = [fake_clone_start,
+ fake_clone_status]
+
+ self.assertRaises(netapp_api.NaApiError, self.client.clone_lun,
+ 'path', 'new_path', 'fakeLUN', 'newFakeLUN')
+
+ def test_clone_lun_multiple_zapi_calls(self):
+ # Max block-ranges per call = 32, max blocks per range = 2^24
+ # Force 2 calls
+ bc = 2 ** 24 * 32 * 2
+ fake_clone_start = netapp_api.NaElement(
+ etree.XML("""<results status="passed">
+ <clone-id>
+ <clone-id-info>
+ <clone-op-id>1337</clone-op-id>
+ <volume-uuid>volume-uuid</volume-uuid>
+ </clone-id-info>
+ </clone-id>
+ </results>"""))
+ fake_clone_status = netapp_api.NaElement(
+ etree.XML("""<results status="passed">
+ <status>
+ <ops-info>
+ <clone-state>completed</clone-state>
+ </ops-info>
+ </status>
+ </results>"""))
+
+ self.connection.invoke_successfully.side_effect = [fake_clone_start,
+ fake_clone_status,
+ fake_clone_start,
+ fake_clone_status]
+
+ self.client.clone_lun('path', 'new_path', 'fakeLUN', 'newFakeLUN',
+ block_count=bc)
+
+ self.assertEqual(4, self.connection.invoke_successfully.call_count)
+
+ def test_clone_lun_wait_for_clone_to_finish(self):
+ # Max block-ranges per call = 32, max blocks per range = 2^24
+ # Force 2 calls
+ bc = 2 ** 24 * 32 * 2
+ fake_clone_start = netapp_api.NaElement(
+ etree.XML("""<results status="passed">
+ <clone-id>
+ <clone-id-info>
+ <clone-op-id>1337</clone-op-id>
+ <volume-uuid>volume-uuid</volume-uuid>
+ </clone-id-info>
+ </clone-id>
+ </results>"""))
+ fake_clone_status = netapp_api.NaElement(
+ etree.XML("""<results status="passed">
+ <status>
+ <ops-info>
+ <clone-state>running</clone-state>
+ </ops-info>
+ </status>
+ </results>"""))
+ fake_clone_status_completed = netapp_api.NaElement(
+ etree.XML("""<results status="passed">
+ <status>
+ <ops-info>
+ <clone-state>completed</clone-state>
+ </ops-info>
+ </status>
+ </results>"""))
+
+ fake_responses = [fake_clone_start,
+ fake_clone_status,
+ fake_clone_status_completed,
+ fake_clone_start,
+ fake_clone_status_completed]
+ self.connection.invoke_successfully.side_effect = fake_responses
+
+ with mock.patch('time.sleep') as mock_sleep:
+ self.client.clone_lun('path', 'new_path', 'fakeLUN',
+ 'newFakeLUN', block_count=bc)
+
+ mock_sleep.assert_called_once_with(1)
+ self.assertEqual(5, self.connection.invoke_successfully.call_count)
+
+ def test_get_lun_by_args(self):
+ response = netapp_api.NaElement(
+ etree.XML("""<results status="passed">
+ <luns>
+ <lun-info></lun-info>
+ </luns>
+ </results>"""))
+ self.connection.invoke_successfully.return_value = response
+
+ luns = self.client.get_lun_by_args()
+
+ self.assertEqual(1, len(luns))
+
+ def test_get_lun_by_args_no_lun_found(self):
+ response = netapp_api.NaElement(
+ etree.XML("""<results status="passed">
+ <luns>
+ </luns>
+ </results>"""))
+ self.connection.invoke_successfully.return_value = response
+
+ luns = self.client.get_lun_by_args()
+
+ self.assertEqual(0, len(luns))
+
+ def test_get_lun_by_args_with_args_specified(self):
+ path = '/vol/%s/%s' % (self.fake_volume, self.fake_lun)
+ response = netapp_api.NaElement(
+ etree.XML("""<results status="passed">
+ <luns>
+ <lun-info></lun-info>
+ </luns>
+ </results>"""))
+ self.connection.invoke_successfully.return_value = response
+
+ lun = self.client.get_lun_by_args(path=path)
+
+ __, _args, __ = self.connection.invoke_successfully.mock_calls[0]
+ actual_request = _args[0]
+ lun_info_args = actual_request.get_children()
+
+ # Assert request is made with correct arguments
+ self.assertEqual('path', lun_info_args[0].get_name())
+ self.assertEqual(path, lun_info_args[0].get_content())
+
+ self.assertEqual(1, len(lun))
+
+ def test_get_filer_volumes(self):
+ response = netapp_api.NaElement(
+ etree.XML("""<results status="passed">
+ <volumes>
+ <volume-info></volume-info>
+ </volumes>
+ </results>"""))
+ self.connection.invoke_successfully.return_value = response
+
+ volumes = self.client.get_filer_volumes()
+
+ self.assertEqual(1, len(volumes))
+
+ def test_get_filer_volumes_no_volumes(self):
+ response = netapp_api.NaElement(
+ etree.XML("""<results status="passed">
+ <volumes>
+ </volumes>
+ </results>"""))
+ self.connection.invoke_successfully.return_value = response
+
+ volumes = self.client.get_filer_volumes()
+
+ self.assertEqual([], volumes)
+
+ def test_get_lun_map(self):
+ path = '/vol/%s/%s' % (self.fake_volume, self.fake_lun)
+ self.connection.invoke_successfully.return_value = mock.Mock()
+
+ self.client.get_lun_map(path=path)
+
+ __, _args, __ = self.connection.invoke_successfully.mock_calls[0]
+ actual_request = _args[0]
+ lun_info_args = actual_request.get_children()
+
+ # Assert request is made with correct arguments
+ self.assertEqual('path', lun_info_args[0].get_name())
+ self.assertEqual(path, lun_info_args[0].get_content())
+
+ def test_set_space_reserve(self):
+ path = '/vol/%s/%s' % (self.fake_volume, self.fake_lun)
+ self.connection.invoke_successfully.return_value = mock.Mock()
+
+ self.client.set_space_reserve(path, 'true')
+
+ __, _args, __ = self.connection.invoke_successfully.mock_calls[0]
+ actual_request = _args[0]
+ lun_info_args = actual_request.get_children()
+
+ # Assert request is made with correct arguments
+ self.assertEqual('path', lun_info_args[0].get_name())
+ self.assertEqual(path, lun_info_args[0].get_content())
+ self.assertEqual('enable', lun_info_args[1].get_name())
+ self.assertEqual('true', lun_info_args[1].get_content())
+
+ def test_get_actual_path_for_export(self):
+ fake_export_path = 'fake_export_path'
+ expected_actual_pathname = 'fake_actual_pathname'
+ response = netapp_api.NaElement(
+ etree.XML("""<results status="passed">
+ <actual-pathname>%(path)s</actual-pathname>
+ </results>""" % {'path': expected_actual_pathname}))
+ self.connection.invoke_successfully.return_value = response
+
+ actual_pathname = self.client.get_actual_path_for_export(
+ fake_export_path)
+
+ self.assertEqual(expected_actual_pathname, actual_pathname)
+
+ def test_clone_file(self):
+ expected_src_path = "fake_src_path"
+ expected_dest_path = "fake_dest_path"
+ fake_volume_id = '0309c748-0d94-41f0-af46-4fbbd76686cf'
+ fake_clone_op_id = 'c22ad299-ecec-4ec0-8de4-352b887bfce2'
+ fake_clone_id_response = netapp_api.NaElement(
+ etree.XML("""<results status="passed">
+ <clone-id>
+ <clone-id-info>
+ <volume-uuid>%(volume)s</volume-uuid>
+ <clone-op-id>%(clone_id)s</clone-op-id>
+ </clone-id-info>
+ </clone-id>
+ </results>""" % {'volume': fake_volume_id,
+ 'clone_id': fake_clone_op_id}))
+ fake_clone_list_response = netapp_api.NaElement(
+ etree.XML("""<results>
+ <clone-list-status>
+ <clone-id-info>
+ <volume-uuid>%(volume)s</volume-uuid>
+ <clone-op-id>%(clone_id)s</clone-op-id>
+ </clone-id-info>
+ <clone-op-id>%(clone_id)s</clone-op-id>
+ </clone-list-status>
+ <status>
+ <ops-info>
+ <clone-state>completed</clone-state>
+ </ops-info>
+ </status>
+ </results>""" % {'volume': fake_volume_id,
+ 'clone_id': fake_clone_op_id}))
+ self.connection.invoke_successfully.side_effect = [
+ fake_clone_id_response, fake_clone_list_response]
+
+ self.client.clone_file(expected_src_path, expected_dest_path)
+
+ __, _args, __ = self.connection.invoke_successfully.mock_calls[0]
+ actual_request = _args[0]
+ actual_src_path = actual_request \
+ .get_child_by_name('source-path').get_content()
+ actual_dest_path = actual_request.get_child_by_name(
+ 'destination-path').get_content()
+
+ self.assertEqual(expected_src_path, actual_src_path)
+ self.assertEqual(expected_dest_path, actual_dest_path)
+ self.assertEqual(actual_request.get_child_by_name(
+ 'destination-exists'), None)
+
+ def test_clone_file_when_clone_fails(self):
+ """Ensure clone is cleaned up on failure."""
+ expected_src_path = "fake_src_path"
+ expected_dest_path = "fake_dest_path"
+ fake_volume_id = '0309c748-0d94-41f0-af46-4fbbd76686cf'
+ fake_clone_op_id = 'c22ad299-ecec-4ec0-8de4-352b887bfce2'
+ fake_clone_id_response = netapp_api.NaElement(
+ etree.XML("""<results status="passed">
+ <clone-id>
+ <clone-id-info>
+ <volume-uuid>%(volume)s</volume-uuid>
+ <clone-op-id>%(clone_id)s</clone-op-id>
+ </clone-id-info>
+ </clone-id>
+ </results>""" % {'volume': fake_volume_id,
+ 'clone_id': fake_clone_op_id}))
+ fake_clone_list_response = netapp_api.NaElement(
+ etree.XML("""<results>
+ <clone-list-status>
+ <clone-id-info>
+ <volume-uuid>%(volume)s</volume-uuid>
+ <clone-op-id>%(clone_id)s</clone-op-id>
+ </clone-id-info>
+ <clone-op-id>%(clone_id)s</clone-op-id>
+ </clone-list-status>
+ <status>
+ <ops-info>
+ <clone-state>failed</clone-state>
+ </ops-info>
+ </status>
+ </results>""" % {'volume': fake_volume_id,
+ 'clone_id': fake_clone_op_id}))
+ fake_clone_clear_response = mock.Mock()
+ self.connection.invoke_successfully.side_effect = [
+ fake_clone_id_response, fake_clone_list_response,
+ fake_clone_clear_response]
+
+ self.assertRaises(netapp_api.NaApiError,
+ self.client.clone_file,
+ expected_src_path,
+ expected_dest_path)
+
+ __, _args, __ = self.connection.invoke_successfully.mock_calls[0]
+ actual_request = _args[0]
+ actual_src_path = actual_request \
+ .get_child_by_name('source-path').get_content()
+ actual_dest_path = actual_request.get_child_by_name(
+ 'destination-path').get_content()
+
+ self.assertEqual(expected_src_path, actual_src_path)
+ self.assertEqual(expected_dest_path, actual_dest_path)
+ self.assertEqual(actual_request.get_child_by_name(
+ 'destination-exists'), None)
+
+ __, _args, __ = self.connection.invoke_successfully.mock_calls[1]
+ actual_request = _args[0]
+ actual_clone_id = actual_request.get_child_by_name('clone-id')
+ actual_clone_id_info = actual_clone_id.get_child_by_name(
+ 'clone-id-info')
+ actual_clone_op_id = actual_clone_id_info.get_child_by_name(
+ 'clone-op-id').get_content()
+ actual_volume_uuid = actual_clone_id_info.get_child_by_name(
+ 'volume-uuid').get_content()
+
+ self.assertEqual(fake_clone_op_id, actual_clone_op_id)
+ self.assertEqual(fake_volume_id, actual_volume_uuid)
+
+ # Ensure that the clone-clear call is made upon error
+ __, _args, __ = self.connection.invoke_successfully.mock_calls[2]
+ actual_request = _args[0]
+ actual_clone_id = actual_request \
+ .get_child_by_name('clone-id').get_content()
+
+ self.assertEqual(fake_clone_op_id, actual_clone_id)
+
+ def test_get_file_usage(self):
+ expected_bytes = "2048"
+ fake_path = 'fake_path'
+ response = netapp_api.NaElement(
+ etree.XML("""<results status="passed">
+ <unique-bytes>%(unique-bytes)s</unique-bytes>
+ </results>""" % {'unique-bytes': expected_bytes}))
+ self.connection.invoke_successfully.return_value = response
+
+ actual_bytes = self.client.get_file_usage(fake_path)
+
+ self.assertEqual(expected_bytes, actual_bytes)
+
+ def test_get_ifconfig(self):
+ expected_response = mock.Mock()
+ self.connection.invoke_successfully.return_value = expected_response
+
+ actual_response = self.client.get_ifconfig()
+
+ __, _args, __ = self.connection.invoke_successfully.mock_calls[0]
+ actual_request = _args[0]
+ self.assertEqual('net-ifconfig-get', actual_request.get_name())
+ self.assertEqual(expected_response, actual_response)
-# Copyright (c) 2014 NetApp, Inc.
+# Copyright (c) - 2014, Alex Meade. All rights reserved.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
import uuid
import mock
+import six
from cinder import exception
+from cinder.i18n import _
from cinder import test
from cinder.tests.test_netapp import create_configuration
import cinder.volume.drivers.netapp.api as ntapi
import cinder.volume.drivers.netapp.utils as na_utils
+FAKE_VOLUME = six.text_type(uuid.uuid4())
+FAKE_LUN = six.text_type(uuid.uuid4())
+FAKE_SIZE = '1024'
+FAKE_METADATA = {'OsType': 'linux', 'SpaceReserved': 'true'}
+
+
class NetAppDirectISCSIDriverTestCase(test.TestCase):
def setUp(self):
self.driver = ntap_iscsi.NetAppDirectISCSIDriver(
configuration=configuration)
self.driver.client = mock.Mock()
- self.fake_volume = str(uuid.uuid4())
- self.fake_lun = str(uuid.uuid4())
- self.fake_size = '1024'
- self.fake_metadata = {'OsType': 'linux', 'SpaceReserved': 'true'}
+ self.driver.zapi_client = mock.Mock()
self.mock_request = mock.Mock()
def _set_config(self, configuration):
'host': 'hostname@backend#vol1'})
warn_msg = 'Extra spec netapp:raid_type is obsolete. ' \
'Use netapp_raid_type instead.'
- na_utils.LOG.warn.assert_called_once_with(warn_msg)
+ na_utils.LOG.warning.assert_called_once_with(warn_msg)
@mock.patch.object(iscsiDriver, 'create_lun', mock.Mock())
@mock.patch.object(iscsiDriver, '_create_lun_handle', mock.Mock())
'host': 'hostname@backend#vol1'})
warn_msg = 'Extra spec netapp_thick_provisioned is deprecated. ' \
'Use netapp_thin_provisioned instead.'
- na_utils.LOG.warn.assert_called_once_with(warn_msg)
-
- def test_create_lun(self):
- expected_path = '/vol/%s/%s' % (self.fake_volume, self.fake_lun)
-
- with mock.patch.object(ntapi.NaElement, 'create_node_with_children',
- return_value=self.mock_request
- ) as mock_create_node:
- self.driver.create_lun(self.fake_volume,
- self.fake_lun,
- self.fake_size,
- self.fake_metadata)
-
- mock_create_node.assert_called_once_with(
- 'lun-create-by-size',
- **{'path': expected_path,
- 'size': self.fake_size,
- 'ostype': self.fake_metadata['OsType'],
- 'space-reservation-enabled':
- self.fake_metadata['SpaceReserved']})
- self.driver.client.invoke_successfully.assert_called_once_with(
- mock.ANY, True)
-
- def test_create_lun_with_qos_policy_group(self):
- expected_path = '/vol/%s/%s' % (self.fake_volume, self.fake_lun)
- expected_qos_group = 'qos_1'
-
- with mock.patch.object(ntapi.NaElement, 'create_node_with_children',
- return_value=self.mock_request
- ) as mock_create_node:
- self.driver.create_lun(self.fake_volume,
- self.fake_lun,
- self.fake_size,
- self.fake_metadata,
- qos_policy_group=expected_qos_group)
-
- mock_create_node.assert_called_once_with(
- 'lun-create-by-size',
- **{'path': expected_path, 'size': self.fake_size,
- 'ostype': self.fake_metadata['OsType'],
- 'space-reservation-enabled':
- self.fake_metadata['SpaceReserved']})
- self.mock_request.add_new_child.assert_called_once_with(
- 'qos-policy-group', expected_qos_group)
- self.driver.client.invoke_successfully.assert_called_once_with(
- mock.ANY, True)
-
- def test_create_lun_raises_on_failure(self):
- self.driver.client.invoke_successfully = mock.Mock(
- side_effect=ntapi.NaApiError)
- self.assertRaises(ntapi.NaApiError,
- self.driver.create_lun,
- self.fake_volume,
- self.fake_lun,
- self.fake_size,
- self.fake_metadata)
+ na_utils.LOG.warning.assert_called_once_with(warn_msg)
def test_update_volume_stats_is_abstract(self):
self.assertRaises(NotImplementedError,
self.driver._update_volume_stats)
+ def test_initialize_connection_no_target_details_found(self):
+ fake_volume = {'name': 'mock-vol'}
+ fake_connector = {'initiator': 'iqn.mock'}
+ self.driver._map_lun = mock.Mock(return_value='mocked-lun-id')
+ self.driver.zapi_client.get_iscsi_service_details = mock.Mock(
+ return_value='mocked-iqn')
+ self.driver.zapi_client.get_target_details = mock.Mock(return_value=[])
+ expected = (_('No iscsi target details were found for LUN %s')
+ % fake_volume['name'])
+ try:
+ self.driver.initialize_connection(fake_volume, fake_connector)
+ except exception.VolumeBackendAPIException as exc:
+ if expected not in six.text_type(exc):
+ self.fail(_('Expected exception message is missing'))
+ else:
+ self.fail(_('VolumeBackendAPIException not raised'))
+
class NetAppiSCSICModeTestCase(test.TestCase):
"""Test case for NetApp's C-Mode iSCSI driver."""
self.driver = ntap_iscsi.NetAppDirectCmodeISCSIDriver(
configuration=mock.Mock())
self.driver.client = mock.Mock()
+ self.driver.zapi_client = mock.Mock()
self.driver.vserver = mock.Mock()
self.driver.ssc_vols = None
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)
-
def test_clone_lun_zero_block_count(self):
"""Test for when clone lun is not passed a block count."""
self.driver._get_lun_attr = mock.Mock(return_value={'Volume':
'fakeLUN'})
- self.driver.client.invoke_successfully = mock.Mock()
+ self.driver.zapi_client = mock.Mock()
+ self.driver.zapi_client.get_lun_by_args.return_value = [
+ mock.Mock(spec=ntapi.NaElement)]
lun = ntapi.NaElement.create_node_with_children(
'lun-info',
**{'alignment': 'indeterminate',
self.driver._clone_lun('fakeLUN', 'newFakeLUN')
- self.assertEqual(1, self.driver.client.invoke_successfully.call_count)
+ self.driver.zapi_client.clone_lun.assert_called_once_with(
+ 'fakeLUN', 'fakeLUN', 'newFakeLUN', 'true', block_count=0,
+ dest_block=0, src_block=0)
@mock.patch.object(ssc_utils, 'refresh_cluster_ssc', mock.Mock())
@mock.patch.object(iscsiCmodeDriver, '_get_pool_stats', mock.Mock())
self.driver.get_volume_stats(refresh=True)
self.assertEqual(na_utils.provide_ems.call_count, 1)
+ def test_create_lun(self):
+ self.driver._update_stale_vols = mock.Mock()
+
+ self.driver.create_lun(FAKE_VOLUME,
+ FAKE_LUN,
+ FAKE_SIZE,
+ FAKE_METADATA)
+
+ self.driver.zapi_client.create_lun.assert_called_once_with(
+ FAKE_VOLUME, FAKE_LUN, FAKE_SIZE,
+ FAKE_METADATA, None)
+
+ self.assertEqual(1, self.driver._update_stale_vols.call_count)
+
class NetAppiSCSI7ModeTestCase(test.TestCase):
"""Test case for NetApp's 7-Mode iSCSI driver."""
self.driver = ntap_iscsi.NetAppDirect7modeISCSIDriver(
configuration=mock.Mock())
self.driver.client = mock.Mock()
+ self.driver.zapi_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)
-
def test_clone_lun_zero_block_count(self):
"""Test for when clone lun is not passed a block count."""
- 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',
'mapped': 'false',
'multiprotocol-type': 'linux',
'online': 'true',
- 'path': '/vol/fakeLUN/lun1',
+ 'path': '/vol/fakeLUN/fakeLUN',
'prefix-size': '0',
'qtree': '',
'read-only': 'false',
'uuid': 'cec1f3d7-3d41-11e2-9cf4-123478563412',
'volume': 'fakeLUN',
'vserver': 'fake_vserver'})
- self.driver._get_lun_by_args = mock.Mock(return_value=[lun])
+ self.driver._get_lun_attr = mock.Mock(return_value={
+ 'Volume': 'fakeLUN', 'Path': '/vol/fake/fakeLUN'})
+ self.driver.zapi_client = mock.Mock()
+ self.driver.zapi_client.get_lun_by_args.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')
- self.assertEqual(1, self.driver.client.invoke_successfully.call_count)
+ self.driver.zapi_client.clone_lun.assert_called_once_with(
+ '/vol/fake/fakeLUN', '/vol/fake/newFakeLUN', 'fakeLUN',
+ 'newFakeLUN', 'true', block_count=0, dest_block=0, src_block=0)
@mock.patch.object(iscsi7modeDriver, '_refresh_volume_info', mock.Mock())
@mock.patch.object(iscsi7modeDriver, '_get_pool_stats', mock.Mock())
def test_vol_stats_calls_provide_ems(self):
self.driver.get_volume_stats(refresh=True)
self.assertEqual(na_utils.provide_ems.call_count, 1)
+
+ def test_create_lun(self):
+ self.driver.vol_refresh_voluntary = False
+
+ self.driver.create_lun(FAKE_VOLUME,
+ FAKE_LUN,
+ FAKE_SIZE,
+ FAKE_METADATA)
+
+ self.driver.zapi_client.create_lun.assert_called_once_with(
+ FAKE_VOLUME, FAKE_LUN, FAKE_SIZE,
+ FAKE_METADATA, None)
+
+ self.assertTrue(self.driver.vol_refresh_voluntary)
Mock unit tests for the NetApp driver utility module
"""
+import six
+
from cinder import test
import cinder.volume.drivers.netapp.utils as na_utils
def test_convert_es_fmt_to_uuid(self):
value = '4Z7JGGVS5VEJBE4LHLGGUUL7VQ'
- result = str(na_utils.convert_es_fmt_to_uuid(value))
+ result = six.text_type(na_utils.convert_es_fmt_to_uuid(value))
self.assertEqual(result, 'e67e931a-b2ed-4890-938b-3acc6a517fac')
def test_round_down(self):
import urllib2
from lxml import etree
+import six
from cinder.i18n import _
from cinder.openstack.common import log as logging
try:
self._api_major_version = int(major)
self._api_minor_version = int(minor)
- self._api_version = str(major) + "." + str(minor)
+ self._api_version = six.text_type(major) + "." + \
+ six.text_type(minor)
except ValueError:
raise ValueError('Major and minor versions must be integers')
self._refresh_conn = True
int(port)
except ValueError:
raise ValueError('Port must be integer')
- self._port = str(port)
+ self._port = six.text_type(port)
self._refresh_conn = True
def get_port(self):
child.add_child_elem(value)
self.add_child_elem(child)
elif isinstance(value, (str, int, float, long)):
- self.add_new_child(key, str(value))
+ self.add_new_child(key, six.text_type(value))
elif isinstance(value, (list, tuple, dict)):
child = NaElement(key)
child.translate_struct(value)
child.translate_struct(data_struct[k])
else:
if data_struct[k]:
- child.set_content(str(data_struct[k]))
+ child.set_content(six.text_type(data_struct[k]))
self.add_child_elem(child)
else:
raise ValueError(_('Type cannot be converted into NaElement.'))
--- /dev/null
+# Copyright (c) - 2014, Alex Meade. All rights reserved.
+# 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.
+
+import sys
+
+from oslo.utils import excutils
+import six
+
+from cinder.i18n import _LE, _LW, _LI
+from cinder.openstack.common import log as logging
+from cinder.volume.drivers.netapp import api as netapp_api
+
+
+LOG = logging.getLogger(__name__)
+
+
+class Client(object):
+
+ def __init__(self, connection):
+ self.connection = connection
+
+ def get_ontapi_version(self):
+ """Gets the supported ontapi version."""
+ ontapi_version = netapp_api.NaElement('system-get-ontapi-version')
+ res = self.connection.invoke_successfully(ontapi_version, False)
+ major = res.get_child_content('major-version')
+ minor = res.get_child_content('minor-version')
+ return (major, minor)
+
+ def create_lun(self, volume_name, lun_name, size, metadata,
+ qos_policy_group=None):
+ """Issues API request for creating LUN on volume."""
+
+ path = '/vol/%s/%s' % (volume_name, lun_name)
+ lun_create = netapp_api.NaElement.create_node_with_children(
+ 'lun-create-by-size',
+ **{'path': path, 'size': six.text_type(size),
+ 'ostype': metadata['OsType'],
+ 'space-reservation-enabled': metadata['SpaceReserved']})
+ if qos_policy_group:
+ lun_create.add_new_child('qos-policy-group', qos_policy_group)
+
+ try:
+ self.connection.invoke_successfully(lun_create, True)
+ except netapp_api.NaApiError as ex:
+ with excutils.save_and_reraise_exception():
+ msg = _LE("Error provisioning volume %(lun_name)s on "
+ "%(volume_name)s. Details: %(ex)s")
+ msg_args = {'lun_name': lun_name,
+ 'volume_name': volume_name,
+ 'ex': six.text_type(ex)}
+ LOG.error(msg % msg_args)
+
+ def destroy_lun(self, path, force=True):
+ """Destroys the lun at the path."""
+ lun_destroy = netapp_api.NaElement.create_node_with_children(
+ 'lun-destroy',
+ **{'path': path})
+ if force:
+ lun_destroy.add_new_child('force', 'true')
+ self.connection.invoke_successfully(lun_destroy, True)
+ seg = path.split("/")
+ LOG.debug("Destroyed LUN %s" % seg[-1])
+
+ def map_lun(self, path, igroup_name, lun_id=None):
+ """Maps lun to the initiator and returns lun id assigned."""
+ lun_map = netapp_api.NaElement.create_node_with_children(
+ 'lun-map', **{'path': path,
+ 'initiator-group': igroup_name})
+ if lun_id:
+ lun_map.add_new_child('lun-id', lun_id)
+ try:
+ result = self.connection.invoke_successfully(lun_map, True)
+ return result.get_child_content('lun-id-assigned')
+ except netapp_api.NaApiError as e:
+ code = e.code
+ message = e.message
+ msg = _LW('Error mapping lun. Code :%(code)s, Message:%(message)s')
+ msg_fmt = {'code': code, 'message': message}
+ LOG.warning(msg % msg_fmt)
+ raise
+
+ def unmap_lun(self, path, igroup_name):
+ """Unmaps a lun from given initiator."""
+ lun_unmap = netapp_api.NaElement.create_node_with_children(
+ 'lun-unmap',
+ **{'path': path, 'initiator-group': igroup_name})
+ try:
+ self.connection.invoke_successfully(lun_unmap, True)
+ except netapp_api.NaApiError as e:
+ msg = _LW("Error unmapping lun. Code :%(code)s,"
+ " Message:%(message)s")
+ msg_fmt = {'code': e.code, 'message': e.message}
+ exc_info = sys.exc_info()
+ LOG.warning(msg % msg_fmt)
+ # if the lun is already unmapped
+ if e.code == '13115' or e.code == '9016':
+ pass
+ else:
+ raise exc_info[0], exc_info[1], exc_info[2]
+
+ def create_igroup(self, igroup, igroup_type='iscsi', os_type='default'):
+ """Creates igroup with specified args."""
+ igroup_create = netapp_api.NaElement.create_node_with_children(
+ 'igroup-create',
+ **{'initiator-group-name': igroup,
+ 'initiator-group-type': igroup_type,
+ 'os-type': os_type})
+ self.connection.invoke_successfully(igroup_create, True)
+
+ def add_igroup_initiator(self, igroup, initiator):
+ """Adds initiators to the specified igroup."""
+ igroup_add = netapp_api.NaElement.create_node_with_children(
+ 'igroup-add',
+ **{'initiator-group-name': igroup,
+ 'initiator': initiator})
+ self.connection.invoke_successfully(igroup_add, True)
+
+ def do_direct_resize(self, path, new_size_bytes, force=True):
+ """Resize the lun."""
+ seg = path.split("/")
+ LOG.info(_LI("Resizing lun %s directly to new size."), seg[-1])
+ lun_resize = netapp_api.NaElement.create_node_with_children(
+ 'lun-resize',
+ **{'path': path,
+ 'size': new_size_bytes})
+ if force:
+ lun_resize.add_new_child('force', 'true')
+ self.connection.invoke_successfully(lun_resize, True)
+
+ def get_lun_geometry(self, path):
+ """Gets the lun geometry."""
+ geometry = {}
+ lun_geo = netapp_api.NaElement("lun-get-geometry")
+ lun_geo.add_new_child('path', path)
+ try:
+ result = self.connection.invoke_successfully(lun_geo, True)
+ geometry['size'] = result.get_child_content("size")
+ geometry['bytes_per_sector'] =\
+ result.get_child_content("bytes-per-sector")
+ geometry['sectors_per_track'] =\
+ result.get_child_content("sectors-per-track")
+ geometry['tracks_per_cylinder'] =\
+ result.get_child_content("tracks-per-cylinder")
+ geometry['cylinders'] =\
+ result.get_child_content("cylinders")
+ geometry['max_resize'] =\
+ result.get_child_content("max-resize-size")
+ except Exception as e:
+ LOG.error(_LE("Lun %(path)s geometry failed. Message - %(msg)s")
+ % {'path': path, 'msg': e.message})
+ return geometry
+
+ def get_volume_options(self, volume_name):
+ """Get the value for the volume option."""
+ opts = []
+ vol_option_list = netapp_api.NaElement("volume-options-list-info")
+ vol_option_list.add_new_child('volume', volume_name)
+ result = self.connection.invoke_successfully(vol_option_list, True)
+ options = result.get_child_by_name("options")
+ if options:
+ opts = options.get_children()
+ return opts
+
+ def move_lun(self, path, new_path):
+ """Moves the lun at path to new path."""
+ seg = path.split("/")
+ new_seg = new_path.split("/")
+ LOG.debug("Moving lun %(name)s to %(new_name)s."
+ % {'name': seg[-1], 'new_name': new_seg[-1]})
+ lun_move = netapp_api.NaElement("lun-move")
+ lun_move.add_new_child("path", path)
+ lun_move.add_new_child("new-path", new_path)
+ self.connection.invoke_successfully(lun_move, True)
+
+ def get_target_details(self):
+ """Gets the target portal details."""
+ raise NotImplementedError()
+
+ def get_iscsi_service_details(self):
+ """Returns iscsi iqn."""
+ raise NotImplementedError()
+
+ def get_lun_list(self):
+ """Gets the list of luns on filer."""
+ raise NotImplementedError()
+
+ def get_igroup_by_initiator(self, initiator):
+ """Get igroups by initiator."""
+ raise NotImplementedError()
+
+ def get_lun_by_args(self, **args):
+ """Retrieves luns with specified args."""
+ raise NotImplementedError()
--- /dev/null
+# Copyright (c) - 2014, Alex Meade. All rights reserved.
+# 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.
+
+
+import copy
+import math
+
+import six
+
+from cinder import exception
+from cinder.i18n import _
+from cinder.openstack.common import log as logging
+from cinder.volume.drivers.netapp import api as netapp_api
+from cinder.volume.drivers.netapp.client import base
+from cinder.volume.drivers.netapp import utils as na_utils
+
+
+LOG = logging.getLogger(__name__)
+
+
+class Client(base.Client):
+
+ def __init__(self, connection, vserver):
+ super(Client, self).__init__(connection)
+ self.vserver = vserver
+
+ def _invoke_vserver_api(self, na_element, vserver):
+ server = copy.copy(self.connection)
+ server.set_vserver(vserver)
+ result = server.invoke_successfully(na_element, True)
+ return result
+
+ def get_target_details(self):
+ """Gets the target portal details."""
+ iscsi_if_iter = netapp_api.NaElement('iscsi-interface-get-iter')
+ result = self.connection.invoke_successfully(iscsi_if_iter, True)
+ tgt_list = []
+ num_records = result.get_child_content('num-records')
+ if num_records and int(num_records) >= 1:
+ attr_list = result.get_child_by_name('attributes-list')
+ iscsi_if_list = attr_list.get_children()
+ for iscsi_if in iscsi_if_list:
+ d = dict()
+ d['address'] = iscsi_if.get_child_content('ip-address')
+ d['port'] = iscsi_if.get_child_content('ip-port')
+ d['tpgroup-tag'] = iscsi_if.get_child_content('tpgroup-tag')
+ d['interface-enabled'] = iscsi_if.get_child_content(
+ 'is-interface-enabled')
+ tgt_list.append(d)
+ return tgt_list
+
+ def get_iscsi_service_details(self):
+ """Returns iscsi iqn."""
+ iscsi_service_iter = netapp_api.NaElement('iscsi-service-get-iter')
+ result = self.connection.invoke_successfully(iscsi_service_iter, True)
+ if result.get_child_content('num-records') and\
+ int(result.get_child_content('num-records')) >= 1:
+ attr_list = result.get_child_by_name('attributes-list')
+ iscsi_service = attr_list.get_child_by_name('iscsi-service-info')
+ return iscsi_service.get_child_content('node-name')
+ LOG.debug('No iSCSI service found for vserver %s' % (self.vserver))
+ return None
+
+ def get_lun_list(self):
+ """Gets the list of luns on filer.
+
+ Gets the luns from cluster with vserver.
+ """
+
+ luns = []
+ tag = None
+ while True:
+ api = netapp_api.NaElement('lun-get-iter')
+ api.add_new_child('max-records', '100')
+ if tag:
+ api.add_new_child('tag', tag, True)
+ lun_info = netapp_api.NaElement('lun-info')
+ lun_info.add_new_child('vserver', self.vserver)
+ query = netapp_api.NaElement('query')
+ query.add_child_elem(lun_info)
+ api.add_child_elem(query)
+ result = self.connection.invoke_successfully(api)
+ if result.get_child_by_name('num-records') and\
+ int(result.get_child_content('num-records')) >= 1:
+ attr_list = result.get_child_by_name('attributes-list')
+ luns.extend(attr_list.get_children())
+ tag = result.get_child_content('next-tag')
+ if tag is None:
+ break
+ return luns
+
+ def get_lun_map(self, path):
+ """Gets the lun map by lun path."""
+ tag = None
+ map_list = []
+ while True:
+ lun_map_iter = netapp_api.NaElement('lun-map-get-iter')
+ lun_map_iter.add_new_child('max-records', '100')
+ if tag:
+ lun_map_iter.add_new_child('tag', tag, True)
+ query = netapp_api.NaElement('query')
+ lun_map_iter.add_child_elem(query)
+ query.add_node_with_children('lun-map-info', **{'path': path})
+ result = self.connection.invoke_successfully(lun_map_iter, True)
+ tag = result.get_child_content('next-tag')
+ if result.get_child_content('num-records') and \
+ int(result.get_child_content('num-records')) >= 1:
+ attr_list = result.get_child_by_name('attributes-list')
+ lun_maps = attr_list.get_children()
+ for lun_map in lun_maps:
+ lun_m = dict()
+ lun_m['initiator-group'] = lun_map.get_child_content(
+ 'initiator-group')
+ lun_m['lun-id'] = lun_map.get_child_content('lun-id')
+ lun_m['vserver'] = lun_map.get_child_content('vserver')
+ map_list.append(lun_m)
+ if tag is None:
+ break
+ return map_list
+
+ def get_igroup_by_initiator(self, initiator):
+ """Get igroups by initiator."""
+ tag = None
+ igroup_list = []
+ while True:
+ igroup_iter = netapp_api.NaElement('igroup-get-iter')
+ igroup_iter.add_new_child('max-records', '100')
+ if tag:
+ igroup_iter.add_new_child('tag', tag, True)
+ query = netapp_api.NaElement('query')
+ igroup_iter.add_child_elem(query)
+ igroup_info = netapp_api.NaElement('initiator-group-info')
+ query.add_child_elem(igroup_info)
+ igroup_info.add_new_child('vserver', self.vserver)
+ initiators = netapp_api.NaElement('initiators')
+ igroup_info.add_child_elem(initiators)
+ initiators.add_node_with_children('initiator-info',
+ **{'initiator-name': initiator})
+ des_attrs = netapp_api.NaElement('desired-attributes')
+ des_ig_info = netapp_api.NaElement('initiator-group-info')
+ des_attrs.add_child_elem(des_ig_info)
+ des_ig_info.add_node_with_children('initiators',
+ **{'initiator-info': None})
+ des_ig_info.add_new_child('vserver', None)
+ des_ig_info.add_new_child('initiator-group-name', None)
+ des_ig_info.add_new_child('initiator-group-type', None)
+ des_ig_info.add_new_child('initiator-group-os-type', None)
+ igroup_iter.add_child_elem(des_attrs)
+ result = self.connection.invoke_successfully(igroup_iter, False)
+ tag = result.get_child_content('next-tag')
+ if result.get_child_content('num-records') and\
+ int(result.get_child_content('num-records')) > 0:
+ attr_list = result.get_child_by_name('attributes-list')
+ igroups = attr_list.get_children()
+ for igroup in igroups:
+ ig = dict()
+ ig['initiator-group-os-type'] = igroup.get_child_content(
+ 'initiator-group-os-type')
+ ig['initiator-group-type'] = igroup.get_child_content(
+ 'initiator-group-type')
+ ig['initiator-group-name'] = igroup.get_child_content(
+ 'initiator-group-name')
+ igroup_list.append(ig)
+ if tag is None:
+ break
+ return igroup_list
+
+ def clone_lun(self, volume, name, new_name, space_reserved='true',
+ src_block=0, dest_block=0, block_count=0):
+ # 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
+ if z_calls == 0:
+ z_calls = 1
+ for call in range(0, z_calls):
+ if zbc > z_limit:
+ block_count = z_limit
+ zbc -= z_limit
+ else:
+ block_count = zbc
+ clone_create = netapp_api.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 = netapp_api.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 =\
+ netapp_api.NaElement.create_node_with_children(
+ 'block-range',
+ **{'source-block-number':
+ six.text_type(src_block),
+ 'destination-block-number':
+ six.text_type(dest_block),
+ 'block-count':
+ six.text_type(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.connection.invoke_successfully(clone_create, True)
+
+ def get_lun_by_args(self, **args):
+ """Retrieves lun with specified args."""
+ lun_iter = netapp_api.NaElement('lun-get-iter')
+ lun_iter.add_new_child('max-records', '100')
+ query = netapp_api.NaElement('query')
+ lun_iter.add_child_elem(query)
+ query.add_node_with_children('lun-info', **args)
+ luns = self.connection.invoke_successfully(lun_iter)
+ attr_list = luns.get_child_by_name('attributes-list')
+ return attr_list.get_children()
+
+ def file_assign_qos(self, flex_vol, qos_policy_group, file_path):
+ """Retrieves lun with specified args."""
+ file_assign_qos = netapp_api.NaElement.create_node_with_children(
+ 'file-assign-qos',
+ **{'volume': flex_vol,
+ 'qos-policy-group-name': qos_policy_group,
+ 'file': file_path,
+ 'vserver': self.vserver})
+ self.connection.invoke_successfully(file_assign_qos, 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')
+ net_if_iter.add_new_child('max-records', '10')
+ query = netapp_api.NaElement('query')
+ net_if_iter.add_child_elem(query)
+ query.add_node_with_children(
+ 'net-interface-info', **{'address': na_utils.resolve_hostname(ip)})
+ result = self.connection.invoke_successfully(net_if_iter, True)
+ num_records = result.get_child_content('num-records')
+ if num_records and int(num_records) >= 1:
+ attr_list = result.get_child_by_name('attributes-list')
+ return attr_list.get_children()
+ raise exception.NotFound(
+ _('No interface found on cluster for ip %s') % (ip))
+
+ def get_vol_by_junc_vserver(self, vserver, junction):
+ """Gets the volume by junction path and vserver."""
+ vol_iter = netapp_api.NaElement('volume-get-iter')
+ vol_iter.add_new_child('max-records', '10')
+ query = netapp_api.NaElement('query')
+ vol_iter.add_child_elem(query)
+ vol_attrs = netapp_api.NaElement('volume-attributes')
+ query.add_child_elem(vol_attrs)
+ vol_attrs.add_node_with_children(
+ 'volume-id-attributes',
+ **{'junction-path': junction,
+ 'owning-vserver-name': vserver})
+ des_attrs = netapp_api.NaElement('desired-attributes')
+ des_attrs.add_node_with_children('volume-attributes',
+ **{'volume-id-attributes': None})
+ vol_iter.add_child_elem(des_attrs)
+ result = self._invoke_vserver_api(vol_iter, vserver)
+ num_records = result.get_child_content('num-records')
+ if num_records and int(num_records) >= 1:
+ attr_list = result.get_child_by_name('attributes-list')
+ vols = attr_list.get_children()
+ vol_id = vols[0].get_child_by_name('volume-id-attributes')
+ return vol_id.get_child_content('name')
+ msg_fmt = {'vserver': vserver, 'junction': junction}
+ raise exception.NotFound(_("No volume on cluster with vserver "
+ "%(vserver)s and junction path "
+ "%(junction)s ") % msg_fmt)
+
+ def clone_file(self, flex_vol, src_path, dest_path, vserver,
+ dest_exists=False):
+ """Clones file on vserver."""
+ msg = ("Cloning with params volume %(volume)s, src %(src_path)s,"
+ "dest %(dest_path)s, vserver %(vserver)s")
+ msg_fmt = {'volume': flex_vol, 'src_path': src_path,
+ 'dest_path': dest_path, 'vserver': vserver}
+ LOG.debug(msg % msg_fmt)
+ clone_create = netapp_api.NaElement.create_node_with_children(
+ 'clone-create',
+ **{'volume': flex_vol, 'source-path': src_path,
+ 'destination-path': dest_path})
+ major, minor = self.connection.get_api_version()
+ if major == 1 and minor >= 20 and dest_exists:
+ clone_create.add_new_child('destination-exists', 'true')
+ self._invoke_vserver_api(clone_create, vserver)
+
+ def get_file_usage(self, path, vserver):
+ """Gets the file unique bytes."""
+ LOG.debug('Getting file usage for %s', path)
+ file_use = netapp_api.NaElement.create_node_with_children(
+ 'file-usage-get', **{'path': path})
+ res = self._invoke_vserver_api(file_use, vserver)
+ unique_bytes = res.get_child_content('unique-bytes')
+ LOG.debug('file-usage for path %(path)s is %(bytes)s'
+ % {'path': path, 'bytes': unique_bytes})
+ return unique_bytes
--- /dev/null
+# Copyright (c) - 2014, Alex Meade. All rights reserved.
+# 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.
+
+
+import copy
+import math
+import time
+
+import six
+
+from cinder import exception
+from cinder.i18n import _, _LW
+from cinder.openstack.common import log as logging
+from cinder.volume.drivers.netapp import api as netapp_api
+from cinder.volume.drivers.netapp.client import base
+
+
+LOG = logging.getLogger(__name__)
+
+
+class Client(base.Client):
+
+ def __init__(self, connection, volume_list=None):
+ super(Client, self).__init__(connection)
+ self.volume_list = volume_list
+
+ def _invoke_vfiler_api(self, na_element, vfiler):
+ server = copy.copy(self.connection)
+ server.set_vfiler(vfiler)
+ result = server.invoke_successfully(na_element, True)
+ return result
+
+ def get_target_details(self):
+ """Gets the target portal details."""
+ iscsi_if_iter = netapp_api.NaElement('iscsi-portal-list-info')
+ result = self.connection.invoke_successfully(iscsi_if_iter, True)
+ tgt_list = []
+ portal_list_entries = result.get_child_by_name(
+ 'iscsi-portal-list-entries')
+ if portal_list_entries:
+ portal_list = portal_list_entries.get_children()
+ for iscsi_if in portal_list:
+ d = dict()
+ d['address'] = iscsi_if.get_child_content('ip-address')
+ d['port'] = iscsi_if.get_child_content('ip-port')
+ d['tpgroup-tag'] = iscsi_if.get_child_content('tpgroup-tag')
+ tgt_list.append(d)
+ return tgt_list
+
+ def get_iscsi_service_details(self):
+ """Returns iscsi iqn."""
+ iscsi_service_iter = netapp_api.NaElement('iscsi-node-get-name')
+ result = self.connection.invoke_successfully(iscsi_service_iter, True)
+ return result.get_child_content('node-name')
+
+ def get_lun_list(self):
+ """Gets the list of luns on filer."""
+ lun_list = []
+ if self.volume_list:
+ for vol in self.volume_list:
+ try:
+ luns = self._get_vol_luns(vol)
+ if luns:
+ lun_list.extend(luns)
+ except netapp_api.NaApiError:
+ LOG.warning(_LW("Error finding luns for volume %s."
+ " Verify volume exists.") % (vol))
+ else:
+ luns = self._get_vol_luns(None)
+ lun_list.extend(luns)
+ return lun_list
+
+ def _get_vol_luns(self, vol_name):
+ """Gets the luns for a volume."""
+ api = netapp_api.NaElement('lun-list-info')
+ if vol_name:
+ api.add_new_child('volume-name', vol_name)
+ result = self.connection.invoke_successfully(api, True)
+ luns = result.get_child_by_name('luns')
+ return luns.get_children()
+
+ def get_igroup_by_initiator(self, initiator):
+ """Get igroups by initiator."""
+ igroup_list = netapp_api.NaElement('igroup-list-info')
+ result = self.connection.invoke_successfully(igroup_list, True)
+ igroups = []
+ igs = result.get_child_by_name('initiator-groups')
+ if igs:
+ ig_infos = igs.get_children()
+ if ig_infos:
+ for info in ig_infos:
+ initiators = info.get_child_by_name('initiators')
+ init_infos = initiators.get_children()
+ if init_infos:
+ for init in init_infos:
+ if init.get_child_content('initiator-name')\
+ == initiator:
+ d = dict()
+ d['initiator-group-os-type'] = \
+ info.get_child_content(
+ 'initiator-group-os-type')
+ d['initiator-group-type'] = \
+ info.get_child_content(
+ 'initiator-group-type')
+ d['initiator-group-name'] = \
+ info.get_child_content(
+ 'initiator-group-name')
+ igroups.append(d)
+ return igroups
+
+ def clone_lun(self, path, clone_path, name, new_name,
+ space_reserved='true', src_block=0,
+ dest_block=0, block_count=0):
+ # 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
+ if z_calls == 0:
+ z_calls = 1
+ for call in range(0, z_calls):
+ if zbc > z_limit:
+ block_count = z_limit
+ zbc -= z_limit
+ else:
+ block_count = zbc
+ clone_start = netapp_api.NaElement.create_node_with_children(
+ 'clone-start', **{'source-path': path,
+ 'destination-path': clone_path,
+ 'no-snap': 'true'})
+ if block_count > 0:
+ block_ranges = netapp_api.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 =\
+ netapp_api.NaElement.create_node_with_children(
+ 'block-range',
+ **{'source-block-number':
+ six.text_type(src_block),
+ 'destination-block-number':
+ six.text_type(dest_block),
+ 'block-count':
+ six.text_type(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.connection.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)
+
+ def _check_clone_status(self, clone_id, vol_uuid, name, new_name):
+ """Checks for the job till completed."""
+ clone_status = netapp_api.NaElement('clone-list-status')
+ cl_id = netapp_api.NaElement('clone-id')
+ clone_status.add_child_elem(cl_id)
+ cl_id.add_node_with_children('clone-id-info',
+ **{'clone-op-id': clone_id,
+ 'volume-uuid': vol_uuid})
+ running = True
+ clone_ops_info = None
+ while running:
+ result = self.connection.invoke_successfully(clone_status, True)
+ status = result.get_child_by_name('status')
+ ops_info = status.get_children()
+ if ops_info:
+ for info in ops_info:
+ if info.get_child_content('clone-state') == 'running':
+ time.sleep(1)
+ break
+ else:
+ running = False
+ clone_ops_info = info
+ break
+ else:
+ if clone_ops_info:
+ fmt = {'name': name, 'new_name': new_name}
+ if clone_ops_info.get_child_content('clone-state')\
+ == 'completed':
+ LOG.debug("Clone operation with src %(name)s"
+ " and dest %(new_name)s completed" % fmt)
+ else:
+ LOG.debug("Clone operation with src %(name)s"
+ " and dest %(new_name)s failed" % fmt)
+ raise netapp_api.NaApiError(
+ clone_ops_info.get_child_content('error'),
+ clone_ops_info.get_child_content('reason'))
+
+ def get_lun_by_args(self, **args):
+ """Retrieves luns with specified args."""
+ lun_info = netapp_api.NaElement.create_node_with_children(
+ 'lun-list-info', **args)
+ result = self.connection.invoke_successfully(lun_info, True)
+ luns = result.get_child_by_name('luns')
+ return luns.get_children()
+
+ def get_filer_volumes(self, volume=None):
+ """Returns list of filer volumes in api format."""
+ vol_request = netapp_api.NaElement('volume-list-info')
+ res = self.connection.invoke_successfully(vol_request, True)
+ volumes = res.get_child_by_name('volumes')
+ if volumes:
+ return volumes.get_children()
+ return []
+
+ def get_lun_map(self, path):
+ lun_map_list = netapp_api.NaElement.create_node_with_children(
+ 'lun-map-list-info',
+ **{'path': path})
+ return self.connection.invoke_successfully(lun_map_list, True)
+
+ def set_space_reserve(self, path, enable):
+ """Sets the space reserve info."""
+ space_res = netapp_api.NaElement.create_node_with_children(
+ 'lun-set-space-reservation-info',
+ **{'path': path, 'enable': enable})
+ self.connection.invoke_successfully(space_res, True)
+
+ def get_actual_path_for_export(self, export_path):
+ """Gets the actual path on the filer for export path."""
+ storage_path = netapp_api.NaElement.create_node_with_children(
+ 'nfs-exportfs-storage-path', **{'pathname': export_path})
+ result = self.connection.invoke_successfully(storage_path)
+ if result.get_child_content('actual-pathname'):
+ return result.get_child_content('actual-pathname')
+ raise exception.NotFound(_('No storage path found for export path %s')
+ % (export_path))
+
+ def clone_file(self, src_path, dest_path):
+ msg_fmt = {'src_path': src_path, 'dest_path': dest_path}
+ LOG.debug("""Cloning with src %(src_path)s, dest %(dest_path)s"""
+ % msg_fmt)
+ clone_start = netapp_api.NaElement.create_node_with_children(
+ 'clone-start',
+ **{'source-path': src_path,
+ 'destination-path': dest_path,
+ 'no-snap': 'true'})
+ result = self.connection.invoke_successfully(clone_start)
+ 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:
+ try:
+ self._wait_for_clone_finish(clone_id, vol_uuid)
+ except netapp_api.NaApiError as e:
+ if e.code != 'UnknownCloneId':
+ self._clear_clone(clone_id)
+ raise e
+
+ def _wait_for_clone_finish(self, clone_op_id, vol_uuid):
+ """Waits till a clone operation is complete or errored out."""
+ clone_ls_st = netapp_api.NaElement('clone-list-status')
+ clone_id = netapp_api.NaElement('clone-id')
+ clone_ls_st.add_child_elem(clone_id)
+ clone_id.add_node_with_children('clone-id-info',
+ **{'clone-op-id': clone_op_id,
+ 'volume-uuid': vol_uuid})
+ task_running = True
+ while task_running:
+ result = self.connection.invoke_successfully(clone_ls_st)
+ status = result.get_child_by_name('status')
+ ops_info = status.get_children()
+ if ops_info:
+ state = ops_info[0].get_child_content('clone-state')
+ if state == 'completed':
+ task_running = False
+ elif state == 'failed':
+ code = ops_info[0].get_child_content('error')
+ reason = ops_info[0].get_child_content('reason')
+ raise netapp_api.NaApiError(code, reason)
+ else:
+ time.sleep(1)
+ else:
+ raise netapp_api.NaApiError(
+ 'UnknownCloneId',
+ 'No clone operation for clone id %s found on the filer'
+ % (clone_id))
+
+ def _clear_clone(self, clone_id):
+ """Clear the clone information.
+
+ Invoke this in case of failed clone.
+ """
+
+ clone_clear = netapp_api.NaElement.create_node_with_children(
+ 'clone-clear',
+ **{'clone-id': clone_id})
+ retry = 3
+ while retry:
+ try:
+ self.connection.invoke_successfully(clone_clear)
+ break
+ except netapp_api.NaApiError:
+ # Filer might be rebooting
+ time.sleep(5)
+ retry = retry - 1
+
+ def get_file_usage(self, path):
+ """Gets the file unique bytes."""
+ LOG.debug('Getting file usage for %s', path)
+ file_use = netapp_api.NaElement.create_node_with_children(
+ 'file-usage-get', **{'path': path})
+ res = self.connection.invoke_successfully(file_use)
+ bytes = res.get_child_content('unique-bytes')
+ LOG.debug('file-usage for path %(path)s is %(bytes)s'
+ % {'path': path, 'bytes': bytes})
+ return bytes
+
+ def get_ifconfig(self):
+ ifconfig = netapp_api.NaElement('net-ifconfig-get')
+ return self.connection.invoke_successfully(ifconfig)
link = "https://communities.netapp.com/groups/openstack"
msg = _("The configured NetApp driver is deprecated."
" Please refer the link to resolve the issue '%s'.")
- LOG.warn(msg % link)
+ LOG.warning(msg % link)
def check_for_setup_error(self):
pass
except exception.NetAppDriverException as e:
LOG.error(_LE("Failure deleting snap vol. Error: %s."), e)
else:
- LOG.warn(_LW("Snapshot volume not found."))
+ LOG.warning(_LW("Snapshot volume not found."))
def _create_snapshot_volume(self, snapshot_id):
"""Creates snapshot volume for given group with snapshot_id."""
try:
self._client.delete_vol_copy_job(job['volcopyRef'])
except exception.NetAppDriverException:
- LOG.warn(_LW("Failure deleting "
- "job %s."), job['volcopyRef'])
+ LOG.warning(_LW("Failure deleting "
+ "job %s."), job['volcopyRef'])
else:
- LOG.warn(_LW('Volume copy job for src vol %s not found.'),
- src_vol['id'])
+ LOG.warning(_LW('Volume copy job for src vol %s not found.'),
+ src_vol['id'])
LOG.info(_LI('Copy job to dest vol %s completed.'), dst_vol['label'])
def create_cloned_volume(self, volume, src_vref):
try:
self.delete_snapshot(snapshot)
except exception.NetAppDriverException:
- LOG.warn(_LW("Failure deleting temp snapshot %s."),
- snapshot['id'])
+ LOG.warning(_LW("Failure deleting temp snapshot %s."),
+ snapshot['id'])
def delete_volume(self, volume):
"""Deletes a volume."""
try:
snap_grp = self._get_cached_snapshot_grp(snapshot['id'])
except KeyError:
- LOG.warn(_LW("Snapshot %s already deleted.") % snapshot['id'])
+ LOG.warning(_LW("Snapshot %s already deleted.") % snapshot['id'])
return
self._client.delete_snapshot_group(snap_grp['pitGroupRef'])
snapshot_name = snap_grp['label']
return self._client.update_host_type(
host['hostRef'], ht_def)
except exception.NetAppDriverException as e:
- msg = _("Unable to update host type for host with"
- " label %(l)s. %(e)s")
- LOG.warn(msg % {'l': host['label'], 'e': e.msg})
+ msg = _LW("Unable to update host type for host with "
+ "label %(l)s. %(e)s")
+ LOG.warning(msg % {'l': host['label'], 'e': e.msg})
return host
except exception.NotFound as e:
- LOG.warn(_LW("Message - %s."), e.msg)
+ LOG.warning(_LW("Message - %s."), e.msg)
return self._create_host(port_id, host_type)
def _get_host_with_port(self, port_id):
(int(x.get('totalRaidedSpace', 0)) -
int(x.get('usedSpace', 0) >= size))]
if not avl_pools:
- msg = _("No storage pool found with available capacity %s.")
- LOG.warn(msg % size_gb)
+ msg = _LW("No storage pool found with available capacity %s.")
+ LOG.warning(msg % size_gb)
return avl_pools
def extend_volume(self, volume, new_size):
"""Removes tmp vols with no snapshots."""
try:
if not utils.set_safe_attr(self, 'clean_job_running', True):
- LOG.warn(_LW('Returning as clean tmp '
- 'vol job already running.'))
+ LOG.warning(_LW('Returning as clean tmp '
+ 'vol job already running.'))
return
for label in self._objects['volumes']['label_ref'].keys():
if (label.startswith('tmp-') and
"""
import copy
-import math
import sys
-import time
import uuid
from oslo.utils import excutils
from cinder.volume.drivers.netapp.api import NaApiError
from cinder.volume.drivers.netapp.api import NaElement
from cinder.volume.drivers.netapp.api import NaServer
+from cinder.volume.drivers.netapp.client import cmode
+from cinder.volume.drivers.netapp.client import seven_mode
from cinder.volume.drivers.netapp.options import netapp_7mode_opts
from cinder.volume.drivers.netapp.options import netapp_basicauth_opts
from cinder.volume.drivers.netapp.options import netapp_cluster_opts
self.configuration.append_config_values(netapp_transport_opts)
self.configuration.append_config_values(netapp_provisioning_opts)
self.lun_table = {}
+ self.zapi_client = None
def _create_client(self, **kwargs):
"""Instantiate a client for NetApp server.
"""
self.lun_table = {}
- self._get_lun_list()
+ lun_list = self.zapi_client.get_lun_list()
+ self._extract_and_populate_luns(lun_list)
LOG.debug("Success getting LUN list from server")
def get_pool(self, volume):
name = volume['name']
metadata = self._get_lun_attr(name, 'metadata')
if not metadata:
- msg = _("No entry in LUN table for volume/snapshot %(name)s.")
+ msg = _LW("No entry in LUN table for volume/snapshot %(name)s.")
msg_fmt = {'name': name}
- LOG.warn(msg % msg_fmt)
+ LOG.warning(msg % msg_fmt)
return
- self._destroy_lun(metadata['Path'])
+ self.zapi_client.destroy_lun(metadata['Path'])
self.lun_table.pop(name)
- def _destroy_lun(self, path, force=True):
- """Destroys the lun at the path."""
- lun_destroy = NaElement.create_node_with_children(
- 'lun-destroy',
- **{'path': path})
- if force:
- lun_destroy.add_new_child('force', 'true')
- self.client.invoke_successfully(lun_destroy, True)
- seg = path.split("/")
- LOG.debug("Destroyed LUN %s" % seg[-1])
-
def ensure_export(self, context, volume):
"""Driver entry point to get the export info for an existing volume."""
handle = self._get_lun_attr(volume['name'], 'handle')
msg = _("Mapped LUN %(name)s to the initiator %(initiator_name)s")
msg_fmt = {'name': name, 'initiator_name': initiator_name}
LOG.debug(msg % msg_fmt)
- iqn = self._get_iscsi_service_details()
- target_details_list = self._get_target_details()
+ iqn = self.zapi_client.get_iscsi_service_details()
+ target_details_list = self.zapi_client.get_target_details()
msg = _("Successfully fetched target details for LUN %(name)s and "
"initiator %(initiator_name)s")
msg_fmt = {'name': name, 'initiator_name': initiator_name}
msg_fmt = {'name': name, 'initiator_name': initiator_name}
LOG.debug(msg % msg_fmt)
- def _get_ontapi_version(self):
- """Gets the supported ontapi version."""
- ontapi_version = NaElement('system-get-ontapi-version')
- res = self.client.invoke_successfully(ontapi_version, False)
- major = res.get_child_content('major-version')
- minor = res.get_child_content('minor-version')
- return (major, minor)
-
def create_lun(self, volume_name, lun_name, size,
metadata, qos_policy_group=None):
- """Issues API request for creating LUN on volume."""
-
- path = '/vol/%s/%s' % (volume_name, lun_name)
- lun_create = NaElement.create_node_with_children(
- 'lun-create-by-size',
- **{'path': path, 'size': six.text_type(size),
- 'ostype': metadata['OsType'],
- 'space-reservation-enabled': metadata['SpaceReserved']})
- if qos_policy_group:
- lun_create.add_new_child('qos-policy-group', qos_policy_group)
-
- try:
- self.client.invoke_successfully(lun_create, True)
- except NaApiError as ex:
- with excutils.save_and_reraise_exception():
- msg = _("Error provisioning volume %(lun_name)s on "
- "%(volume_name)s. Details: %(ex)s")
- msg_args = {'lun_name': lun_name,
- 'volume_name': volume_name,
- 'ex': six.text_type(ex)}
- LOG.error(msg % msg_args)
-
- def _get_iscsi_service_details(self):
- """Returns iscsi iqn."""
- raise NotImplementedError()
-
- def _get_target_details(self):
- """Gets the target portal details."""
+ """Creates a LUN, handling ONTAP differences as needed."""
raise NotImplementedError()
def _create_lun_handle(self, metadata):
"""Returns lun handle based on filer type."""
raise NotImplementedError()
- def _get_lun_list(self):
- """Gets the list of luns on filer."""
- raise NotImplementedError()
-
def _extract_and_populate_luns(self, api_luns):
"""Extracts the luns from api.
os = 'default'
igroup_name = self._get_or_create_igroup(initiator,
initiator_type, os)
- lun_map = NaElement.create_node_with_children(
- 'lun-map', **{'path': path,
- 'initiator-group': igroup_name})
- if lun_id:
- lun_map.add_new_child('lun-id', lun_id)
try:
- result = self.client.invoke_successfully(lun_map, True)
- return result.get_child_content('lun-id-assigned')
- except NaApiError as e:
- code = e.code
- message = e.message
- msg = _('Error mapping lun. Code :%(code)s, Message:%(message)s')
- msg_fmt = {'code': code, 'message': message}
+ return self.zapi_client.map_lun(path, igroup_name, lun_id=lun_id)
+ except NaApiError:
exc_info = sys.exc_info()
- LOG.warn(msg % msg_fmt)
(_igroup, lun_id) = self._find_mapped_lun_igroup(path, initiator)
if lun_id is not None:
return lun_id
def _unmap_lun(self, path, initiator):
"""Unmaps a lun from given initiator."""
(igroup_name, _lun_id) = self._find_mapped_lun_igroup(path, initiator)
- lun_unmap = NaElement.create_node_with_children(
- 'lun-unmap',
- **{'path': path, 'initiator-group': igroup_name})
- try:
- self.client.invoke_successfully(lun_unmap, True)
- except NaApiError as e:
- msg = _("Error unmapping lun. Code :%(code)s,"
- " Message:%(message)s")
- msg_fmt = {'code': e.code, 'message': e.message}
- exc_info = sys.exc_info()
- LOG.warn(msg % msg_fmt)
- # if the lun is already unmapped
- if e.code == '13115' or e.code == '9016':
- pass
- else:
- raise exc_info[0], exc_info[1], exc_info[2]
+ self.zapi_client.unmap_lun(path, igroup_name)
def _find_mapped_lun_igroup(self, path, initiator, os=None):
"""Find the igroup for mapped lun with initiator."""
Creates igroup if not found.
"""
- igroups = self._get_igroup_by_initiator(initiator=initiator)
+ igroups = self.zapi_client.get_igroup_by_initiator(initiator=initiator)
igroup_name = None
for igroup in igroups:
if igroup['initiator-group-os-type'] == os:
igroup_name = igroup['initiator-group-name']
break
if not igroup_name:
- igroup_name = self.IGROUP_PREFIX + str(uuid.uuid4())
- self._create_igroup(igroup_name, initiator_type, os)
- self._add_igroup_initiator(igroup_name, initiator)
+ igroup_name = self.IGROUP_PREFIX + six.text_type(uuid.uuid4())
+ self.zapi_client.create_igroup(igroup_name, initiator_type, os)
+ self.zapi_client.add_igroup_initiator(igroup_name, initiator)
return igroup_name
- def _get_igroup_by_initiator(self, initiator):
- """Get igroups by initiator."""
- raise NotImplementedError()
-
def _check_allowed_os(self, os):
"""Checks if the os type supplied is NetApp supported."""
if os in ['linux', 'aix', 'hpux', 'windows', 'solaris',
else:
return False
- def _create_igroup(self, igroup, igroup_type='iscsi', os_type='default'):
- """Creates igroup with specified args."""
- igroup_create = NaElement.create_node_with_children(
- 'igroup-create',
- **{'initiator-group-name': igroup,
- 'initiator-group-type': igroup_type,
- 'os-type': os_type})
- self.client.invoke_successfully(igroup_create, True)
-
- def _add_igroup_initiator(self, igroup, initiator):
- """Adds initiators to the specified igroup."""
- igroup_add = NaElement.create_node_with_children(
- 'igroup-add',
- **{'initiator-group-name': igroup,
- 'initiator': initiator})
- self.client.invoke_successfully(igroup_add, True)
-
def _add_lun_to_table(self, lun):
"""Adds LUN to cache table."""
if not isinstance(lun, NetAppLun):
"""
lun = self.lun_table.get(name)
if lun is None:
- self._get_lun_list()
+ lun_list = self.zapi_client.get_lun_list()
+ self._extract_and_populate_luns(lun_list)
lun = self.lun_table.get(name)
if lun is None:
raise exception.VolumeNotFound(volume_id=name)
"""Clone LUN with the given name to the new name."""
raise NotImplementedError()
- def _get_lun_by_args(self, **args):
- """Retrieves luns with specified args."""
- raise NotImplementedError()
-
def _get_lun_attr(self, name, attr):
"""Get the lun attribute if found else None."""
try:
name = volume['name']
lun = self._get_lun_from_table(name)
path = lun.metadata['Path']
- curr_size_bytes = str(lun.size)
- new_size_bytes = str(int(new_size) * units.Gi)
+ curr_size_bytes = six.text_type(lun.size)
+ new_size_bytes = six.text_type(int(new_size) * units.Gi)
# Reused by clone scenarios.
# Hence comparing the stored size.
if curr_size_bytes != new_size_bytes:
- lun_geometry = self._get_lun_geometry(path)
+ lun_geometry = self.zapi_client.get_lun_geometry(path)
if (lun_geometry and lun_geometry.get("max_resize")
and int(lun_geometry.get("max_resize")) >=
int(new_size_bytes)):
- self._do_direct_resize(path, new_size_bytes)
+ self.zapi_client.do_direct_resize(path, new_size_bytes)
else:
self._do_sub_clone_resize(path, new_size_bytes)
self.lun_table[name].size = new_size_bytes
LOG.info(_LI("No need to extend volume %s"
" as it is already the requested new size."), name)
- def _do_direct_resize(self, path, new_size_bytes, force=True):
- """Uses the resize api to resize the lun."""
- seg = path.split("/")
- LOG.info(_LI("Resizing lun %s directly to new size."), seg[-1])
- lun_resize = NaElement("lun-resize")
- lun_resize.add_new_child('path', path)
- lun_resize.add_new_child('size', new_size_bytes)
- if force:
- lun_resize.add_new_child('force', 'true')
- self.client.invoke_successfully(lun_resize, True)
-
- def _get_lun_geometry(self, path):
- """Gets the lun geometry."""
- geometry = {}
- lun_geo = NaElement("lun-get-geometry")
- lun_geo.add_new_child('path', path)
- try:
- result = self.client.invoke_successfully(lun_geo, True)
- geometry['size'] = result.get_child_content("size")
- geometry['bytes_per_sector'] =\
- result.get_child_content("bytes-per-sector")
- geometry['sectors_per_track'] =\
- result.get_child_content("sectors-per-track")
- geometry['tracks_per_cylinder'] =\
- result.get_child_content("tracks-per-cylinder")
- geometry['cylinders'] =\
- result.get_child_content("cylinders")
- geometry['max_resize'] =\
- result.get_child_content("max-resize-size")
- except Exception as e:
- LOG.error(_LE("Lun %(path)s geometry failed. Message - %(msg)s")
- % {'path': path, 'msg': e.message})
- return geometry
-
- def _get_volume_options(self, volume_name):
- """Get the value for the volume option."""
- opts = []
- vol_option_list = NaElement("volume-options-list-info")
- vol_option_list.add_new_child('volume', volume_name)
- result = self.client.invoke_successfully(vol_option_list, True)
- options = result.get_child_by_name("options")
- if options:
- opts = options.get_children()
- return opts
-
def _get_vol_option(self, volume_name, option_name):
"""Get the value for the volume option."""
value = None
- options = self._get_volume_options(volume_name)
+ options = self.zapi_client.get_volume_options(volume_name)
for opt in options:
if opt.get_child_content('name') == option_name:
value = opt.get_child_content('value')
break
return value
- def _move_lun(self, path, new_path):
- """Moves the lun at path to new path."""
- seg = path.split("/")
- new_seg = new_path.split("/")
- LOG.debug("Moving lun %(name)s to %(new_name)s."
- % {'name': seg[-1], 'new_name': new_seg[-1]})
- lun_move = NaElement("lun-move")
- lun_move.add_new_child("path", path)
- lun_move.add_new_child("new-path", new_path)
- self.client.invoke_successfully(lun_move, True)
-
def _do_sub_clone_resize(self, path, new_size_bytes):
"""Does sub lun clone after verification.
' as it contains no blocks.')
raise exception.VolumeBackendAPIException(data=msg % name)
new_lun = 'new-%s' % (name)
- self.create_lun(vol_name, new_lun, new_size_bytes, metadata)
+ self.zapi_client.create_lun(vol_name, new_lun, new_size_bytes,
+ metadata)
try:
self._clone_lun(name, new_lun, block_count=block_count)
self._post_sub_clone_resize(path)
except Exception:
with excutils.save_and_reraise_exception():
new_path = '/vol/%s/%s' % (vol_name, new_lun)
- self._destroy_lun(new_path)
+ self.zapi_client.destroy_lun(new_path)
def _post_sub_clone_resize(self, path):
"""Try post sub clone resize in a transactional manner."""
tmp_path = "/vol/%s/%s" % (seg[2], tmp_lun)
new_path = "/vol/%s/%s" % (seg[2], new_lun)
try:
- st_tm_mv = self._move_lun(path, tmp_path)
- st_nw_mv = self._move_lun(new_path, path)
- st_del_old = self._destroy_lun(tmp_path)
+ st_tm_mv = self.zapi_client.move_lun(path, tmp_path)
+ st_nw_mv = self.zapi_client.move_lun(new_path, path)
+ st_del_old = self.zapi_client.destroy_lun(tmp_path)
except Exception as e:
if st_tm_mv is None:
msg = _("Failure staging lun %s to tmp.")
raise exception.VolumeBackendAPIException(data=msg % (seg[-1]))
else:
if st_nw_mv is None:
- self._move_lun(tmp_path, path)
+ self.zapi_client.move_lun(tmp_path, path)
msg = _("Failure moving new cloned lun to %s.")
raise exception.VolumeBackendAPIException(
data=msg % (seg[-1]))
"""Gets block counts for the lun."""
LOG.debug("Getting lun block count.")
block_count = 0
- lun_infos = self._get_lun_by_args(path=path)
+ lun_infos = self.zapi_client.get_lun_by_args(path=path)
if not lun_infos:
seg = path.split('/')
msg = _('Failure getting lun info for %s.')
"""Does custom setup for ontap cluster."""
self.vserver = self.configuration.netapp_vserver
self.vserver = self.vserver if self.vserver else self.DEFAULT_VS
+ self.zapi_client = cmode.Client(self.client, self.vserver)
# We set vserver in client permanently.
# To use tunneling enable_tunneling while invoking api
self.client.set_vserver(self.vserver)
# Default values to run first api
self.client.set_api_version(1, 15)
- (major, minor) = self._get_ontapi_version()
+ (major, minor) = self.zapi_client.get_ontapi_version()
self.client.set_api_version(major, minor)
self.ssc_vols = None
self.stale_vols = set()
metadata, qos_policy_group=None):
"""Creates a LUN, handling ONTAP differences as needed."""
- super(NetAppDirectCmodeISCSIDriver, self).create_lun(
+ self.zapi_client.create_lun(
volume_name, lun_name, size, metadata, qos_policy_group)
self._update_stale_vols(
volume=ssc_utils.NetAppVolume(volume_name, self.vserver))
- def _get_target_details(self):
- """Gets the target portal details."""
- iscsi_if_iter = NaElement('iscsi-interface-get-iter')
- result = self.client.invoke_successfully(iscsi_if_iter, True)
- tgt_list = []
- if result.get_child_content('num-records')\
- and int(result.get_child_content('num-records')) >= 1:
- attr_list = result.get_child_by_name('attributes-list')
- iscsi_if_list = attr_list.get_children()
- for iscsi_if in iscsi_if_list:
- d = dict()
- d['address'] = iscsi_if.get_child_content('ip-address')
- d['port'] = iscsi_if.get_child_content('ip-port')
- d['tpgroup-tag'] = iscsi_if.get_child_content('tpgroup-tag')
- d['interface-enabled'] = iscsi_if.get_child_content(
- 'is-interface-enabled')
- tgt_list.append(d)
- return tgt_list
-
- def _get_iscsi_service_details(self):
- """Returns iscsi iqn."""
- iscsi_service_iter = NaElement('iscsi-service-get-iter')
- result = self.client.invoke_successfully(iscsi_service_iter, True)
- if result.get_child_content('num-records') and\
- int(result.get_child_content('num-records')) >= 1:
- attr_list = result.get_child_by_name('attributes-list')
- iscsi_service = attr_list.get_child_by_name('iscsi-service-info')
- return iscsi_service.get_child_content('node-name')
- LOG.debug('No iscsi service found for vserver %s' % (self.vserver))
- return None
-
def _create_lun_handle(self, metadata):
"""Returns lun handle based on filer type."""
return '%s:%s' % (self.vserver, metadata['Path'])
- def _get_lun_list(self):
- """Gets the list of luns on filer.
-
- Gets the luns from cluster with vserver.
- """
-
- tag = None
- while True:
- api = NaElement('lun-get-iter')
- api.add_new_child('max-records', '100')
- if tag:
- api.add_new_child('tag', tag, True)
- lun_info = NaElement('lun-info')
- lun_info.add_new_child('vserver', self.vserver)
- query = NaElement('query')
- query.add_child_elem(lun_info)
- api.add_child_elem(query)
- result = self.client.invoke_successfully(api)
- if result.get_child_by_name('num-records') and\
- int(result.get_child_content('num-records')) >= 1:
- attr_list = result.get_child_by_name('attributes-list')
- self._extract_and_populate_luns(attr_list.get_children())
- tag = result.get_child_content('next-tag')
- if tag is None:
- break
-
def _find_mapped_lun_igroup(self, path, initiator, os=None):
"""Find the igroup for mapped lun with initiator."""
- initiator_igroups = self._get_igroup_by_initiator(initiator=initiator)
- lun_maps = self._get_lun_map(path)
+ initiator_igroups = self.zapi_client.get_igroup_by_initiator(
+ initiator=initiator)
+ lun_maps = self.zapi_client.get_lun_map(path)
if initiator_igroups and lun_maps:
for igroup in initiator_igroups:
igroup_name = igroup['initiator-group-name']
return (igroup_name, lun_map['lun-id'])
return (None, None)
- def _get_lun_map(self, path):
- """Gets the lun map by lun path."""
- tag = None
- map_list = []
- while True:
- lun_map_iter = NaElement('lun-map-get-iter')
- lun_map_iter.add_new_child('max-records', '100')
- if tag:
- lun_map_iter.add_new_child('tag', tag, True)
- query = NaElement('query')
- lun_map_iter.add_child_elem(query)
- query.add_node_with_children('lun-map-info', **{'path': path})
- result = self.client.invoke_successfully(lun_map_iter, True)
- tag = result.get_child_content('next-tag')
- if result.get_child_content('num-records') and \
- int(result.get_child_content('num-records')) >= 1:
- attr_list = result.get_child_by_name('attributes-list')
- lun_maps = attr_list.get_children()
- for lun_map in lun_maps:
- lun_m = dict()
- lun_m['initiator-group'] = lun_map.get_child_content(
- 'initiator-group')
- lun_m['lun-id'] = lun_map.get_child_content('lun-id')
- lun_m['vserver'] = lun_map.get_child_content('vserver')
- map_list.append(lun_m)
- if tag is None:
- break
- return map_list
-
- def _get_igroup_by_initiator(self, initiator):
- """Get igroups by initiator."""
- tag = None
- igroup_list = []
- while True:
- igroup_iter = NaElement('igroup-get-iter')
- igroup_iter.add_new_child('max-records', '100')
- if tag:
- igroup_iter.add_new_child('tag', tag, True)
- query = NaElement('query')
- igroup_iter.add_child_elem(query)
- igroup_info = NaElement('initiator-group-info')
- query.add_child_elem(igroup_info)
- igroup_info.add_new_child('vserver', self.vserver)
- initiators = NaElement('initiators')
- igroup_info.add_child_elem(initiators)
- initiators.add_node_with_children('initiator-info',
- **{'initiator-name': initiator})
- des_attrs = NaElement('desired-attributes')
- des_ig_info = NaElement('initiator-group-info')
- des_attrs.add_child_elem(des_ig_info)
- des_ig_info.add_node_with_children('initiators',
- **{'initiator-info': None})
- des_ig_info.add_new_child('vserver', None)
- des_ig_info.add_new_child('initiator-group-name', None)
- des_ig_info.add_new_child('initiator-group-type', None)
- des_ig_info.add_new_child('initiator-group-os-type', None)
- igroup_iter.add_child_elem(des_attrs)
- result = self.client.invoke_successfully(igroup_iter, False)
- tag = result.get_child_content('next-tag')
- if result.get_child_content('num-records') and\
- int(result.get_child_content('num-records')) > 0:
- attr_list = result.get_child_by_name('attributes-list')
- igroups = attr_list.get_children()
- for igroup in igroups:
- ig = dict()
- ig['initiator-group-os-type'] = igroup.get_child_content(
- 'initiator-group-os-type')
- ig['initiator-group-type'] = igroup.get_child_content(
- 'initiator-group-type')
- ig['initiator-group-name'] = igroup.get_child_content(
- 'initiator-group-name')
- igroup_list.append(ig)
- if tag is None:
- break
- return igroup_list
-
def _clone_lun(self, name, new_name, space_reserved='true',
src_block=0, dest_block=0, block_count=0):
"""Clone LUN with the given handle to the new name."""
metadata = self._get_lun_attr(name, 'metadata')
volume = metadata['Volume']
- # 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
- if z_calls == 0:
- z_calls = 1
- 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)
+ self.zapi_client.clone_lun(volume, name, new_name, space_reserved,
+ src_block=0, dest_block=0, block_count=0)
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))
+ lun = self.zapi_client.get_lun_by_args(vserver=self.vserver,
+ path='/vol/%s/%s'
+ % (volume, new_name))
if len(lun) == 0:
msg = _("No cloned lun named %s found on the filer")
raise exception.VolumeBackendAPIException(data=msg % (new_name))
self._update_stale_vols(
volume=ssc_utils.NetAppVolume(volume, self.vserver))
- def _get_lun_by_args(self, **args):
- """Retrieves lun with specified args."""
- lun_iter = NaElement('lun-get-iter')
- lun_iter.add_new_child('max-records', '100')
- query = NaElement('query')
- lun_iter.add_child_elem(query)
- query.add_node_with_children('lun-info', **args)
- luns = self.client.invoke_successfully(lun_iter)
- attr_list = luns.get_child_by_name('attributes-list')
- return attr_list.get_children()
-
def _create_lun_meta(self, lun):
"""Creates lun metadata dictionary."""
self._is_naelement(lun)
if self.volume_list:
self.volume_list = self.volume_list.split(',')
self.volume_list = [el.strip() for el in self.volume_list]
- (major, minor) = self._get_ontapi_version()
+ self.zapi_client = seven_mode.Client(self.client, self.volume_list)
+ (major, minor) = self.zapi_client.get_ontapi_version()
self.client.set_api_version(major, minor)
if self.vfiler:
self.client.set_vfiler(self.vfiler)
metadata, qos_policy_group=None):
"""Creates a LUN, handling ONTAP differences as needed."""
- super(NetAppDirect7modeISCSIDriver, self).create_lun(
+ self.zapi_client.create_lun(
volume_name, lun_name, size, metadata, qos_policy_group)
self.vol_refresh_voluntary = True
- def _get_filer_volumes(self, volume=None):
- """Returns list of filer volumes in api format."""
- vol_request = NaElement('volume-list-info')
- if volume:
- vol_request.add_new_child('volume', volume)
- res = self.client.invoke_successfully(vol_request, True)
- volumes = res.get_child_by_name('volumes')
- if volumes:
- return volumes.get_children()
- return []
-
def _get_root_volume_name(self):
# switch to volume-get-root-name API when possible
- vols = self._get_filer_volumes()
+ vols = self.zapi_client.get_filer_volumes()
for vol in vols:
volume_name = vol.get_child_content('name')
if self._get_vol_option(volume_name, 'root') == 'true':
return volume_name
- LOG.warn(_LW('Could not determine root volume name '
- 'on %s.') % self._get_owner())
+ LOG.warning(_LW('Could not determine root volume name '
+ 'on %s.') % self._get_owner())
return None
- def _get_igroup_by_initiator(self, initiator):
- """Get igroups by initiator."""
- igroup_list = NaElement('igroup-list-info')
- result = self.client.invoke_successfully(igroup_list, True)
- igroups = []
- igs = result.get_child_by_name('initiator-groups')
- if igs:
- ig_infos = igs.get_children()
- if ig_infos:
- for info in ig_infos:
- initiators = info.get_child_by_name('initiators')
- init_infos = initiators.get_children()
- if init_infos:
- for init in init_infos:
- if init.get_child_content('initiator-name')\
- == initiator:
- d = dict()
- d['initiator-group-os-type'] = \
- info.get_child_content(
- 'initiator-group-os-type')
- d['initiator-group-type'] = \
- info.get_child_content(
- 'initiator-group-type')
- d['initiator-group-name'] = \
- info.get_child_content(
- 'initiator-group-name')
- igroups.append(d)
- return igroups
-
- def _get_target_details(self):
- """Gets the target portal details."""
- iscsi_if_iter = NaElement('iscsi-portal-list-info')
- result = self.client.invoke_successfully(iscsi_if_iter, True)
- tgt_list = []
- portal_list_entries = result.get_child_by_name(
- 'iscsi-portal-list-entries')
- if portal_list_entries:
- portal_list = portal_list_entries.get_children()
- for iscsi_if in portal_list:
- d = dict()
- d['address'] = iscsi_if.get_child_content('ip-address')
- d['port'] = iscsi_if.get_child_content('ip-port')
- d['tpgroup-tag'] = iscsi_if.get_child_content('tpgroup-tag')
- tgt_list.append(d)
- return tgt_list
-
- def _get_iscsi_service_details(self):
- """Returns iscsi iqn."""
- iscsi_service_iter = NaElement('iscsi-node-get-name')
- result = self.client.invoke_successfully(iscsi_service_iter, True)
- return result.get_child_content('node-name')
-
def _get_owner(self):
if self.vfiler:
owner = '%s:%s' % (self.configuration.netapp_server_hostname,
owner = self._get_owner()
return '%s:%s' % (owner, metadata['Path'])
- def _get_lun_list(self):
- """Gets the list of luns on filer."""
- lun_list = []
- if self.volume_list:
- for vol in self.volume_list:
- try:
- luns = self._get_vol_luns(vol)
- if luns:
- lun_list.extend(luns)
- except NaApiError:
- LOG.warn(_LW("Error finding luns for volume %s."
- " Verify volume exists.") % (vol))
- else:
- luns = self._get_vol_luns(None)
- lun_list.extend(luns)
- self._extract_and_populate_luns(lun_list)
-
- def _get_vol_luns(self, vol_name):
- """Gets the luns for a volume."""
- api = NaElement('lun-list-info')
- if vol_name:
- api.add_new_child('volume-name', vol_name)
- result = self.client.invoke_successfully(api, True)
- luns = result.get_child_by_name('luns')
- return luns.get_children()
-
def _find_mapped_lun_igroup(self, path, initiator, os=None):
"""Find the igroup for mapped lun with initiator."""
- lun_map_list = NaElement.create_node_with_children(
- 'lun-map-list-info',
- **{'path': path})
- result = self.client.invoke_successfully(lun_map_list, True)
+ result = self.zapi_client.get_lun_map(path)
igroups = result.get_child_by_name('initiator-groups')
if igroups:
igroup = None
path = metadata['Path']
(parent, _splitter, name) = path.rpartition('/')
clone_path = '%s/%s' % (parent, 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
- if z_calls == 0:
- z_calls = 1
- 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.zapi_client.clone_lun(path, clone_path, name, new_name,
+ space_reserved, src_block=0,
+ dest_block=0, block_count=0)
+
self.vol_refresh_voluntary = True
- luns = self._get_lun_by_args(path=clone_path)
+ luns = self.zapi_client.get_lun_by_args(path=clone_path)
if luns:
cloned_lun = luns[0]
- self._set_space_reserve(clone_path, space_reserved)
+ self.zapi_client.set_space_reserve(clone_path, space_reserved)
clone_meta = self._create_lun_meta(cloned_lun)
handle = self._create_lun_handle(clone_meta)
self._add_lun_to_table(
else:
raise NaApiError('ENOLUNENTRY', 'No Lun entry found on the filer')
- def _set_space_reserve(self, path, enable):
- """Sets the space reserve info."""
- space_res = NaElement.create_node_with_children(
- 'lun-set-space-reservation-info',
- **{'path': path, 'enable': enable})
- self.client.invoke_successfully(space_res, True)
-
- def _check_clone_status(self, clone_id, vol_uuid, name, new_name):
- """Checks for the job till completed."""
- clone_status = NaElement('clone-list-status')
- cl_id = NaElement('clone-id')
- clone_status.add_child_elem(cl_id)
- cl_id.add_node_with_children(
- 'clone-id-info',
- **{'clone-op-id': clone_id, 'volume-uuid': vol_uuid})
- running = True
- clone_ops_info = None
- while running:
- result = self.client.invoke_successfully(clone_status, True)
- status = result.get_child_by_name('status')
- ops_info = status.get_children()
- if ops_info:
- for info in ops_info:
- if info.get_child_content('clone-state') == 'running':
- time.sleep(1)
- break
- else:
- running = False
- clone_ops_info = info
- break
- else:
- if clone_ops_info:
- fmt = {'name': name, 'new_name': new_name}
- if clone_ops_info.get_child_content('clone-state')\
- == 'completed':
- LOG.debug("Clone operation with src %(name)s"
- " and dest %(new_name)s completed" % fmt)
- else:
- LOG.debug("Clone operation with src %(name)s"
- " and dest %(new_name)s failed" % fmt)
- raise NaApiError(
- clone_ops_info.get_child_content('error'),
- clone_ops_info.get_child_content('reason'))
-
- def _get_lun_by_args(self, **args):
- """Retrieves luns with specified args."""
- lun_info = NaElement.create_node_with_children('lun-list-info', **args)
- result = self.client.invoke_successfully(lun_info, True)
- luns = result.get_child_by_name('luns')
- return luns.get_children()
-
def _create_lun_meta(self, lun):
"""Creates lun metadata dictionary."""
self._is_naelement(lun)
try:
job_set = set_safe_attr(self, 'vol_refresh_running', True)
if not job_set:
- LOG.warn(_LW("Volume refresh job already "
- "running. Returning..."))
+ LOG.warning(_LW("Volume refresh job already "
+ "running. Returning..."))
return
self.vol_refresh_voluntary = False
- self.vols = self._get_filer_volumes()
+ self.vols = self.zapi_client.get_filer_volumes()
self.vol_refresh_time = timeutils.utcnow()
except Exception as e:
- LOG.warn(_LW("Error refreshing volume info. Message: %s"),
- six.text_type(e))
+ LOG.warning(_LW("Error refreshing volume info. Message: %s"),
+ six.text_type(e))
finally:
set_safe_attr(self, 'vol_refresh_running', False)
Volume driver for NetApp NFS storage.
"""
-import copy
import os
import re
from threading import Timer
from cinder.image import image_utils
from cinder.openstack.common import log as logging
from cinder import utils
-from cinder.volume.drivers.netapp.api import NaApiError
from cinder.volume.drivers.netapp.api import NaElement
from cinder.volume.drivers.netapp.api import NaServer
+from cinder.volume.drivers.netapp.client import cmode
+from cinder.volume.drivers.netapp.client import seven_mode
from cinder.volume.drivers.netapp.options import netapp_basicauth_opts
from cinder.volume.drivers.netapp.options import netapp_cluster_opts
from cinder.volume.drivers.netapp.options import netapp_connection_opts
volume['name'], file_name,
volume['provider_location'], file_name)
except Exception as e:
- LOG.warn(_LW('Exception while registering image %(image_id)s'
- ' in cache. Exception: %(exc)s')
- % {'image_id': image_id, 'exc': e.__str__()})
+ LOG.warning(_LW('Exception while registering image %(image_id)s'
+ ' in cache. Exception: %(exc)s')
+ % {'image_id': image_id, 'exc': e.__str__()})
def _find_image_in_cache(self, image_id):
"""Finds image in cache and returns list of shares with file name."""
LOG.debug('Image cache cleaning in progress.')
thres_size_perc_start =\
self.configuration.thres_avl_size_perc_start
- thres_size_perc_stop =\
+ thres_size_perc_stop = \
self.configuration.thres_avl_size_perc_stop
for share in getattr(self, '_mounted_shares', []):
try:
- total_size, total_avl, _total_alc =\
+ total_size, total_avl, _total_alc = \
self._get_capacity_info(share)
avl_percent = int((total_avl / total_size) * 100)
if avl_percent <= thres_size_perc_start:
else:
continue
except Exception as e:
- LOG.warn(_LW('Exception during cache cleaning'
- ' %(share)s. Message - %(ex)s')
- % {'share': share, 'ex': e.__str__()})
+ LOG.warning(_LW('Exception during cache cleaning'
+ ' %(share)s. Message - %(ex)s')
+ % {'share': share, 'ex': e.__str__()})
continue
finally:
LOG.debug('Image cache cleaning done.')
if self._delete_file(file_path):
return True
return False
+
if _do_delete():
bytes_to_free = bytes_to_free - int(f[1])
if bytes_to_free <= 0:
volume['provider_location'] = share
break
except Exception:
- LOG.warn(_LW('Unexpected exception during'
- ' image cloning in share %s'), share)
+ LOG.warning(_LW('Unexpected exception during'
+ ' image cloning in share %s'), share)
return cloned
def _direct_nfs_clone(self, volume, image_location, image_id):
if data.file_format != "raw":
raise exception.InvalidResults(
_("Converted to raw, but"
- " format is now %s") % data.file_format)
+ " format is now %s") % data.file_format)
else:
cloned = True
self._register_image_in_cache(
return True
else:
if retry_seconds <= 0:
- LOG.warn(_LW('Discover file retries exhausted.'))
+ LOG.warning(_LW('Discover file retries exhausted.'))
return False
else:
time.sleep(sleep_interval)
"""
conn, dr = None, None
if image_location:
- nfs_loc_pattern =\
+ nfs_loc_pattern = \
('^nfs://(([\w\-\.]+:{1}[\d]+|[\w\-\.]+)(/[^\/].*)'
'*(/[^\/\\\\]+)$)')
matched = re.match(nfs_loc_pattern, image_location, flags=0)
share_candidates)
return self._share_match_for_ip(ip, share_candidates)
except Exception:
- LOG.warn(_LW("Unexpected exception while short "
- "listing used share."))
+ LOG.warning(_LW("Unexpected exception while short "
+ "listing used share."))
return None
def _construct_image_nfs_url(self, image_location):
def _move_nfs_file(self, source_path, dest_path):
"""Moves source to destination."""
+
@utils.synchronized(dest_path, external=True)
def _move_file(src, dst):
if os.path.exists(dst):
- LOG.warn(_LW("Destination %s already exists."), dst)
+ LOG.warning(_LW("Destination %s already exists."), dst)
return False
self._execute('mv', src, dst,
run_as_root=self._execute_as_root)
try:
return _move_file(source_path, dest_path)
except Exception as e:
- LOG.warn(_LW('Exception moving file %(src)s. Message - %(e)s')
- % {'src': source_path, 'e': e})
+ LOG.warning(_LW('Exception moving file %(src)s. Message - %(e)s')
+ % {'src': source_path, 'e': e})
return False
-class NetAppDirectNfsDriver (NetAppNFSDriver):
+class NetAppDirectNfsDriver(NetAppNFSDriver):
"""Executes commands related to volumes on NetApp filer."""
def __init__(self, *args, **kwargs):
if not isinstance(elem, NaElement):
raise ValueError('Expects NaElement')
- def _get_ontapi_version(self):
- """Gets the supported ontapi version."""
- ontapi_version = NaElement('system-get-ontapi-version')
- res = self._client.invoke_successfully(ontapi_version, False)
- major = res.get_child_content('major-version')
- minor = res.get_child_content('minor-version')
- return (major, minor)
-
def _get_export_ip_path(self, volume_id=None, share=None):
"""Returns export ip and path.
'apparent_available': apparent_available}
-class NetAppDirectCmodeNfsDriver (NetAppDirectNfsDriver):
+class NetAppDirectCmodeNfsDriver(NetAppDirectNfsDriver):
"""Executes commands related to volumes on c mode."""
def __init__(self, *args, **kwargs):
"""Do the customized set up on client for cluster mode."""
# Default values to run first api
client.set_api_version(1, 15)
- (major, minor) = self._get_ontapi_version()
- client.set_api_version(major, minor)
self.vserver = self.configuration.netapp_vserver
+ self.zapi_client = cmode.Client(client, self.vserver)
+ (major, minor) = self.zapi_client.get_ontapi_version()
+ client.set_api_version(major, minor)
self.ssc_vols = None
self.stale_vols = set()
if self.vserver:
self.ssc_enabled = True
LOG.info(_LI("Shares on vserver %s will only"
- " be used for provisioning.") % (self.vserver))
+ " be used for provisioning.") % self.vserver)
else:
self.ssc_enabled = False
- LOG.warn(_LW("No vserver set in config. SSC will be disabled."))
+ LOG.warning(_LW("No vserver set in config. "
+ "SSC will be disabled."))
def check_for_setup_error(self):
"""Check that the driver is working and can communicate."""
if self.ssc_enabled:
ssc_utils.check_ssc_api_permissions(self._client)
- def _invoke_successfully(self, na_element, vserver=None):
- """Invoke the api for successful result.
-
- If vserver is present then invokes vserver api
- else Cluster api.
- :param vserver: vserver name.
- """
-
- self._is_naelement(na_element)
- server = copy.copy(self._client)
- if vserver:
- server.set_vserver(vserver)
- else:
- server.set_vserver(None)
- result = server.invoke_successfully(na_element, True)
- return result
-
def create_volume(self, volume):
"""Creates a volume.
def _set_qos_policy_group_on_volume(self, volume, share, qos_policy_group):
target_path = '%s' % (volume['name'])
export_path = share.split(':')[1]
- flex_vol_name = self._get_vol_by_junc_vserver(self.vserver,
- export_path)
- file_assign_qos = NaElement.create_node_with_children(
- 'file-assign-qos',
- **{'volume': flex_vol_name,
- 'qos-policy-group-name': qos_policy_group,
- 'file': target_path,
- 'vserver': self.vserver})
- self._invoke_successfully(file_assign_qos)
+ flex_vol_name = self.zapi_client.get_vol_by_junc_vserver(self.vserver,
+ export_path)
+ self.zapi_client.file_assign_qos(flex_vol_name,
+ qos_policy_group,
+ target_path)
def _clone_volume(self, volume_name, clone_name,
volume_id, share=None):
"""Clones mounted volume on NetApp Cluster."""
(vserver, exp_volume) = self._get_vserver_and_exp_vol(volume_id, share)
- self._clone_file(exp_volume, volume_name, clone_name, vserver)
+ self.zapi_client.clone_file(exp_volume, volume_name, clone_name,
+ vserver)
share = share if share else self._get_provider_location(volume_id)
self._post_prov_deprov_in_ssc(share)
def _get_vserver_and_exp_vol(self, volume_id=None, share=None):
"""Gets the vserver and export volume for share."""
(host_ip, export_path) = self._get_export_ip_path(volume_id, share)
- ifs = self._get_if_info_by_ip(host_ip)
+ ifs = self.zapi_client.get_if_info_by_ip(host_ip)
vserver = ifs[0].get_child_content('vserver')
- exp_volume = self._get_vol_by_junc_vserver(vserver, export_path)
+ exp_volume = self.zapi_client.get_vol_by_junc_vserver(vserver,
+ export_path)
return (vserver, exp_volume)
- def _get_if_info_by_ip(self, ip):
- """Gets the network interface info by ip."""
- net_if_iter = NaElement('net-interface-get-iter')
- net_if_iter.add_new_child('max-records', '10')
- query = NaElement('query')
- net_if_iter.add_child_elem(query)
- query.add_node_with_children(
- 'net-interface-info', **{'address': na_utils.resolve_hostname(ip)})
- result = self._invoke_successfully(net_if_iter)
- if result.get_child_content('num-records') and\
- int(result.get_child_content('num-records')) >= 1:
- attr_list = result.get_child_by_name('attributes-list')
- return attr_list.get_children()
- raise exception.NotFound(
- _('No interface found on cluster for ip %s')
- % (ip))
-
def _get_vserver_ips(self, vserver):
"""Get ips for the vserver."""
result = na_utils.invoke_api(
if_list.extend(ifs)
return if_list
- def _get_vol_by_junc_vserver(self, vserver, junction):
- """Gets the volume by junction path and vserver."""
- vol_iter = NaElement('volume-get-iter')
- vol_iter.add_new_child('max-records', '10')
- query = NaElement('query')
- vol_iter.add_child_elem(query)
- vol_attrs = NaElement('volume-attributes')
- query.add_child_elem(vol_attrs)
- vol_attrs.add_node_with_children(
- 'volume-id-attributes',
- **{'junction-path': junction,
- 'owning-vserver-name': vserver})
- des_attrs = NaElement('desired-attributes')
- des_attrs.add_node_with_children('volume-attributes',
- **{'volume-id-attributes': None})
- vol_iter.add_child_elem(des_attrs)
- result = self._invoke_successfully(vol_iter, vserver)
- if result.get_child_content('num-records') and\
- int(result.get_child_content('num-records')) >= 1:
- attr_list = result.get_child_by_name('attributes-list')
- vols = attr_list.get_children()
- vol_id = vols[0].get_child_by_name('volume-id-attributes')
- return vol_id.get_child_content('name')
- msg_fmt = {'vserver': vserver, 'junction': junction}
- raise exception.NotFound(_("""No volume on cluster with vserver
- %(vserver)s and junction path %(junction)s
- """) % msg_fmt)
-
- def _clone_file(self, volume, src_path, dest_path, vserver=None,
- dest_exists=False):
- """Clones file on vserver."""
- msg = _("""Cloning with params volume %(volume)s, src %(src_path)s,
- dest %(dest_path)s, vserver %(vserver)s""")
- msg_fmt = {'volume': volume, 'src_path': src_path,
- 'dest_path': dest_path, 'vserver': vserver}
- LOG.debug(msg % msg_fmt)
- clone_create = NaElement.create_node_with_children(
- 'clone-create',
- **{'volume': volume, 'source-path': src_path,
- 'destination-path': dest_path})
- major, minor = self._client.get_api_version()
- if major == 1 and minor >= 20 and dest_exists:
- clone_create.add_new_child('destination-exists', 'true')
- self._invoke_successfully(clone_create, vserver)
-
def _update_volume_stats(self):
"""Retrieve stats info from vserver."""
def refresh_ssc_vols(self, vols):
"""Refreshes ssc_vols with latest entries."""
if not self._mounted_shares:
- LOG.warn(_LW("No shares found hence skipping ssc refresh."))
+ LOG.warning(_LW("No shares found hence skipping ssc refresh."))
return
mnt_share_vols = set()
vs_ifs = self._get_vserver_ips(self.vserver)
volume_id=None, share=share)
for file in old_files:
path = '/vol/%s/%s' % (exp_volume, file)
- u_bytes = self._get_cluster_file_usage(path, vserver)
+ u_bytes = self.zapi_client.get_file_usage(path, vserver)
file_list.append((file, u_bytes))
LOG.debug('Shortlisted del elg files %s', file_list)
return file_list
- def _get_cluster_file_usage(self, path, vserver):
- """Gets the file unique bytes."""
- LOG.debug('Getting file usage for %s', path)
- file_use = NaElement.create_node_with_children(
- 'file-usage-get', **{'path': path})
- res = self._invoke_successfully(file_use, vserver)
- bytes = res.get_child_content('unique-bytes')
- LOG.debug('file-usage for path %(path)s is %(bytes)s'
- % {'path': path, 'bytes': bytes})
- return bytes
-
def _share_match_for_ip(self, ip, shares):
"""Returns the share that is served by ip.
def _get_vserver_for_ip(self, ip):
"""Get vserver for the mentioned ip."""
try:
- ifs = self._get_if_info_by_ip(ip)
+ ifs = self.zapi_client.get_if_info_by_ip(ip)
vserver = ifs[0].get_child_content('vserver')
return vserver
except Exception:
dest_exists=False):
"""Clone file even if dest exists."""
(vserver, exp_volume) = self._get_vserver_and_exp_vol(share=share)
- self._clone_file(exp_volume, src_name, dst_name, vserver,
- dest_exists=dest_exists)
+ self.zapi_client.clone_file(exp_volume, src_name, dst_name, vserver,
+ dest_exists=dest_exists)
def _copy_from_img_service(self, context, volume, image_service,
image_id):
dst_ip = self._get_ip_verify_on_cluster(self._get_host_ip(
volume['id']))
# tmp file is required to deal with img formats
- tmp_img_file = str(uuid.uuid4())
+ tmp_img_file = six.text_type(uuid.uuid4())
col_path = self.configuration.netapp_copyoffload_tool_path
img_info = image_service.show(context, image_id)
dst_share = self._get_provider_location(volume['id'])
% {'img': image_id, 'vol': volume['id']})
else:
LOG.debug('Image will be converted to raw %s.', image_id)
- img_conv = str(uuid.uuid4())
+ img_conv = six.text_type(uuid.uuid4())
dst_img_conv_local = os.path.join(dst_dir, img_conv)
# Checking against image size which is approximate check
self._delete_file(dst_img_local)
-class NetAppDirect7modeNfsDriver (NetAppDirectNfsDriver):
+class NetAppDirect7modeNfsDriver(NetAppDirectNfsDriver):
"""Executes commands related to volumes on 7 mode."""
def __init__(self, *args, **kwargs):
def _do_custom_setup(self, client):
"""Do the customized set up on client if any for 7 mode."""
- (major, minor) = self._get_ontapi_version()
+ self.zapi_client = seven_mode.Client(client)
+ (major, minor) = self.zapi_client.get_ontapi_version()
client.set_api_version(major, minor)
def check_for_setup_error(self):
raise exception.VolumeBackendAPIException(data=msg)
super(NetAppDirect7modeNfsDriver, self).check_for_setup_error()
- def _invoke_successfully(self, na_element, vfiler=None):
- """Invoke the api for successful result.
-
- If vfiler is present then invokes vfiler api
- else filer api.
- :param vfiler: vfiler name.
- """
-
- self._is_naelement(na_element)
- server = copy.copy(self._client)
- if vfiler:
- server.set_vfiler(vfiler)
- else:
- server.set_vfiler(None)
- result = server.invoke_successfully(na_element, True)
- return result
-
def create_volume(self, volume):
"""Creates a volume.
volume_id, share=None):
"""Clones mounted volume with NetApp filer."""
(_host_ip, export_path) = self._get_export_ip_path(volume_id, share)
- storage_path = self._get_actual_path_for_export(export_path)
+ storage_path = self.zapi_client.get_actual_path_for_export(export_path)
target_path = '%s/%s' % (storage_path, clone_name)
- (clone_id, vol_uuid) = self._start_clone('%s/%s' % (storage_path,
- volume_name),
- target_path)
- if vol_uuid:
- try:
- self._wait_for_clone_finish(clone_id, vol_uuid)
- except NaApiError as e:
- if e.code != 'UnknownCloneId':
- self._clear_clone(clone_id)
- raise e
-
- def _get_actual_path_for_export(self, export_path):
- """Gets the actual path on the filer for export path."""
- storage_path = NaElement.create_node_with_children(
- 'nfs-exportfs-storage-path', **{'pathname': export_path})
- result = self._invoke_successfully(storage_path, None)
- if result.get_child_content('actual-pathname'):
- return result.get_child_content('actual-pathname')
- raise exception.NotFound(_('No storage path found for export path %s')
- % (export_path))
-
- def _start_clone(self, src_path, dest_path):
- """Starts the clone operation.
-
- :returns: clone-id
- """
-
- msg_fmt = {'src_path': src_path, 'dest_path': dest_path}
- LOG.debug("""Cloning with src %(src_path)s, dest %(dest_path)s"""
- % msg_fmt)
- clone_start = NaElement.create_node_with_children(
- 'clone-start',
- **{'source-path': src_path,
- 'destination-path': dest_path,
- 'no-snap': 'true'})
- result = self._invoke_successfully(clone_start, None)
- 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')
- return (clone_id, vol_uuid)
-
- def _wait_for_clone_finish(self, clone_op_id, vol_uuid):
- """Waits till a clone operation is complete or errored out."""
- clone_ls_st = NaElement('clone-list-status')
- clone_id = NaElement('clone-id')
- clone_ls_st.add_child_elem(clone_id)
- clone_id.add_node_with_children('clone-id-info',
- **{'clone-op-id': clone_op_id,
- 'volume-uuid': vol_uuid})
- task_running = True
- while task_running:
- result = self._invoke_successfully(clone_ls_st, None)
- status = result.get_child_by_name('status')
- ops_info = status.get_children()
- if ops_info:
- state = ops_info[0].get_child_content('clone-state')
- if state == 'completed':
- task_running = False
- elif state == 'failed':
- code = ops_info[0].get_child_content('error')
- reason = ops_info[0].get_child_content('reason')
- raise NaApiError(code, reason)
- else:
- time.sleep(1)
- else:
- raise NaApiError(
- 'UnknownCloneId',
- 'No clone operation for clone id %s found on the filer'
- % (clone_id))
-
- def _clear_clone(self, clone_id):
- """Clear the clone information.
-
- Invoke this in case of failed clone.
- """
-
- clone_clear = NaElement.create_node_with_children(
- 'clone-clear',
- **{'clone-id': clone_id})
- retry = 3
- while retry:
- try:
- self._invoke_successfully(clone_clear, None)
- break
- except Exception:
- # Filer might be rebooting
- time.sleep(5)
- retry = retry - 1
+ self.zapi_client.clone_file('%s/%s' % (storage_path, volume_name),
+ target_path)
def _update_volume_stats(self):
"""Retrieve stats info from vserver."""
def _shortlist_del_eligible_files(self, share, old_files):
"""Prepares list of eligible files to be deleted from cache."""
file_list = []
- exp_volume = self._get_actual_path_for_export(share)
+ exp_volume = self.zapi_client.get_actual_path_for_export(share)
for file in old_files:
path = '/vol/%s/%s' % (exp_volume, file)
- u_bytes = self._get_filer_file_usage(path)
+ u_bytes = self.zapi_client.get_file_usage(path)
file_list.append((file, u_bytes))
LOG.debug('Shortlisted del elg files %s', file_list)
return file_list
- def _get_filer_file_usage(self, path):
- """Gets the file unique bytes."""
- LOG.debug('Getting file usage for %s', path)
- file_use = NaElement.create_node_with_children(
- 'file-usage-get', **{'path': path})
- res = self._invoke_successfully(file_use)
- bytes = res.get_child_content('unique-bytes')
- LOG.debug('file-usage for path %(path)s is %(bytes)s'
- % {'path': path, 'bytes': bytes})
- return bytes
-
def _is_filer_ip(self, ip):
"""Checks whether ip is on the same filer."""
try:
- ifconfig = NaElement('net-ifconfig-get')
- res = self._invoke_successfully(ifconfig, None)
- if_info = res.get_child_by_name('interface-config-info')
+ ifconfig = self.zapi_client.get_ifconfig()
+ if_info = ifconfig.get_child_by_name('interface-config-info')
if if_info:
ifs = if_info.get_children()
for intf in ifs:
import six
from cinder import exception
-from cinder.i18n import _
+from cinder.i18n import _, _LW
from cinder.openstack.common import log as logging
from cinder import utils
from cinder.volume import driver
backend = args[0]
na_server = args[1]
vserver = args[2]
- identity = str(id(backend))
+ identity = six.text_type(id(backend))
lock_pr = '%s_%s' % ('refresh_ssc', identity)
try:
job_set = na_utils.set_safe_attr(
backend = args[0]
na_server = args[1]
vserver = args[2]
- identity = str(id(backend))
+ identity = six.text_type(id(backend))
lock_pr = '%s_%s' % ('refresh_ssc', identity)
# As this depends on stale job running state
raise exception.InvalidInput(reason=_("Backend server not NaServer."))
delta_secs = getattr(backend, 'ssc_run_delta_secs', 1800)
if getattr(backend, 'ssc_job_running', None):
- LOG.warn(_('ssc job in progress. Returning... '))
+ LOG.warning(_LW('ssc job in progress. Returning... '))
return
elif (getattr(backend, 'ssc_run_time', None) is None or
(backend.ssc_run_time and
args=[backend, na_server, vserver])
t.start()
elif getattr(backend, 'refresh_stale_running', None):
- LOG.warn(_('refresh stale ssc job in progress. Returning... '))
+ LOG.warning(_LW('refresh stale ssc job in progress. Returning... '))
return
else:
if backend.stale_vols:
unsupp_ssc_features = []
for fail in failed_apis:
unsupp_ssc_features.extend(api_map[fail])
- LOG.warn(_("The user does not have access or sufficient"
- " privileges to use all netapp apis. The following"
- " extra_specs will fail or be ignored: %s"),
- unsupp_ssc_features)
+ LOG.warning(_LW("The user does not have access or sufficient "
+ "privileges to use all netapp apis. The "
+ "following extra_specs will fail or be ignored: "
+ "%s"), unsupp_ssc_features)
from cinder import context
from cinder import exception
-from cinder.i18n import _
+from cinder.i18n import _, _LW
from cinder.openstack.common import log as logging
from cinder import utils
from cinder import version
na_server.invoke_successfully(ems, True)
LOG.debug("ems executed successfully.")
except NaApiError as e:
- LOG.warn(_("Failed to invoke ems. Message : %s") % e)
+ LOG.warning(_LW("Failed to invoke ems. Message : %s") % e)
finally:
requester.last_ems = timeutils.utcnow()
"""
if kwargs and kwargs.get('netapp_mode') == 'proxy':
return
- LOG.warn(_("It is not the recommended way to use drivers by NetApp. "
- "Please use NetAppDriver to achieve the functionality."))
+ LOG.warning(_LW("It is not the recommended way to use drivers by NetApp. "
+ "Please use NetAppDriver to achieve the functionality."))
def invoke_api(na_server, api_name, api_family='cm', query=None,
if additional_elems:
api_el.translate_struct(additional_elems)
if is_iter:
- api_el.add_new_child('max-records', str(record_step))
+ api_el.add_new_child('max-records', six.text_type(record_step))
if tag:
api_el.add_new_child('tag', tag, True)
return api_el
def to_bool(val):
"""Converts true, yes, y, 1 to True, False otherwise."""
if val:
- strg = str(val).lower()
+ strg = six.text_type(val).lower()
if (strg == 'true' or strg == 'y'
or strg == 'yes' or strg == 'enabled'
or strg == '1'):
def convert_uuid_to_es_fmt(uuid_str):
"""Converts uuid to e-series compatible name format."""
- uuid_base32 = encode_hex_to_base32(uuid.UUID(str(uuid_str)).hex)
+ uuid_base32 = encode_hex_to_base32(uuid.UUID(six.text_type(uuid_str)).hex)
return uuid_base32.strip('=')
def log_extra_spec_warnings(extra_specs):
for spec in (set(extra_specs.keys() if extra_specs else []) &
set(OBSOLETE_SSC_SPECS.keys())):
- msg = _('Extra spec %(old)s is obsolete. Use %(new)s instead.')
+ msg = _LW('Extra spec %(old)s is obsolete. Use %(new)s instead.')
args = {'old': spec, 'new': OBSOLETE_SSC_SPECS[spec]}
- LOG.warn(msg % args)
+ LOG.warning(msg % args)
for spec in (set(extra_specs.keys() if extra_specs else []) &
set(DEPRECATED_SSC_SPECS.keys())):
- msg = _('Extra spec %(old)s is deprecated. Use %(new)s instead.')
+ msg = _LW('Extra spec %(old)s is deprecated. Use %(new)s '
+ 'instead.')
args = {'old': spec, 'new': DEPRECATED_SSC_SPECS[spec]}
- LOG.warn(msg % args)
+ LOG.warning(msg % args)
class OpenStackInfo(object):