configuration = conf.Configuration(None)
configuration.append_config_values(options.netapp_basicauth_opts)
configuration.append_config_values(options.netapp_eseries_opts)
+ configuration.append_config_values(options.netapp_san_opts)
return configuration
def test_setup_error_unsupported_host_type(self):
configuration = self._set_config(create_configuration())
- configuration.netapp_eseries_host_type = 'garbage'
+ configuration.netapp_host_type = 'garbage'
driver = common.NetAppDriver(configuration=configuration)
self.assertRaises(exception.NetAppDriverException,
driver.check_for_setup_error)
+ def test_check_host_type_default(self):
+ configuration = self._set_config(create_configuration())
+ driver = common.NetAppDriver(configuration=configuration)
+ driver._check_host_type()
+ self.assertEqual('LnxALUA', driver.host_type)
+
def test_do_setup_all_default(self):
configuration = self._set_config(create_configuration())
driver = common.NetAppDriver(configuration=configuration)
import mock
from cinder import exception
+from cinder.i18n import _
from cinder import test
from cinder.tests.volume.drivers.netapp.dataontap import fakes as fake
from cinder.volume.drivers.netapp.dataontap import block_base
def test_map_lun(self, mock_get_or_create_igroup, mock_get_lun_attr):
os = 'linux'
protocol = 'fcp'
+ self.library.host_type = 'linux'
mock_get_lun_attr.return_value = {'Path': fake.LUN1, 'OsType': os}
- mock_get_or_create_igroup.return_value = fake.IGROUP1_NAME
+ mock_get_or_create_igroup.return_value = (fake.IGROUP1_NAME, os,
+ 'iscsi')
self.zapi_client.map_lun.return_value = '1'
lun_id = self.library._map_lun('fake_volume',
self.zapi_client.map_lun.assert_called_once_with(
fake.LUN1, fake.IGROUP1_NAME, lun_id=None)
+ @mock.patch.object(block_base.NetAppBlockStorageLibrary, '_get_lun_attr')
+ @mock.patch.object(block_base.NetAppBlockStorageLibrary,
+ '_get_or_create_igroup')
+ @mock.patch.object(block_base, 'LOG', mock.Mock())
+ def test_map_lun_mismatch_host_os(
+ self, mock_get_or_create_igroup, mock_get_lun_attr):
+ os = 'windows'
+ protocol = 'fcp'
+ self.library.host_type = 'linux'
+ mock_get_lun_attr.return_value = {'Path': fake.LUN1, 'OsType': os}
+ mock_get_or_create_igroup.return_value = (fake.IGROUP1_NAME, os,
+ 'iscsi')
+ self.library._map_lun('fake_volume',
+ fake.FC_FORMATTED_INITIATORS,
+ protocol, None)
+ mock_get_or_create_igroup.assert_called_once_with(
+ fake.FC_FORMATTED_INITIATORS, protocol,
+ self.library.host_type)
+ self.zapi_client.map_lun.assert_called_once_with(
+ fake.LUN1, fake.IGROUP1_NAME, lun_id=None)
+ self.assertEqual(1, block_base.LOG.warning.call_count)
+
@mock.patch.object(block_base.NetAppBlockStorageLibrary,
'_get_lun_attr')
@mock.patch.object(block_base.NetAppBlockStorageLibrary,
os = 'linux'
protocol = 'fcp'
mock_get_lun_attr.return_value = {'Path': fake.LUN1, 'OsType': os}
- mock_get_or_create_igroup.return_value = fake.IGROUP1_NAME
+ mock_get_or_create_igroup.return_value = (fake.IGROUP1_NAME, os,
+ 'iscsi')
mock_find_mapped_lun_igroup.return_value = (fake.IGROUP1_NAME, '2')
self.zapi_client.map_lun.side_effect = netapp_api.NaApiError
os = 'linux'
protocol = 'fcp'
mock_get_lun_attr.return_value = {'Path': fake.LUN1, 'OsType': os}
- mock_get_or_create_igroup.return_value = fake.IGROUP1_NAME
+ mock_get_or_create_igroup.return_value = (fake.IGROUP1_NAME, os,
+ 'iscsi')
mock_find_mapped_lun_igroup.return_value = (None, None)
self.zapi_client.map_lun.side_effect = netapp_api.NaApiError
def test_get_or_create_igroup_preexisting(self):
self.zapi_client.get_igroup_by_initiators.return_value = [fake.IGROUP1]
-
- igroup_name = self.library._get_or_create_igroup(
+ self.library._create_igroup_add_initiators = mock.Mock()
+ igroup_name, host_os, ig_type = self.library._get_or_create_igroup(
fake.FC_FORMATTED_INITIATORS, 'fcp', 'linux')
- self.assertEqual(igroup_name, fake.IGROUP1_NAME)
+ self.assertEqual(fake.IGROUP1_NAME, igroup_name)
+ self.assertEqual('linux', host_os)
+ self.assertEqual('fcp', ig_type)
self.zapi_client.get_igroup_by_initiators.assert_called_once_with(
fake.FC_FORMATTED_INITIATORS)
+ self.assertEqual(
+ 0, self.library._create_igroup_add_initiators.call_count)
@mock.patch.object(uuid, 'uuid4', mock.Mock(return_value=fake.UUID1))
def test_get_or_create_igroup_none_preexisting(self):
+ """This method also tests _create_igroup_add_initiators."""
self.zapi_client.get_igroup_by_initiators.return_value = []
- igroup_name = self.library._get_or_create_igroup(
+ igroup_name, os, ig_type = self.library._get_or_create_igroup(
fake.FC_FORMATTED_INITIATORS, 'fcp', 'linux')
self.assertEqual(igroup_name, 'openstack-' + fake.UUID1)
igroup_name, 'fcp', 'linux')
self.assertEqual(len(fake.FC_FORMATTED_INITIATORS),
self.zapi_client.add_igroup_initiator.call_count)
+ self.assertEqual('linux', os)
+ self.assertEqual('fcp', ig_type)
def test_get_fc_target_wwpns(self):
self.assertRaises(NotImplementedError,
na_utils.LOG.warning.assert_called_once_with(warn_msg)
@mock.patch.object(na_utils, 'check_flags')
- def test_do_setup(self, mock_check_flags):
+ def test_do_setup_san_configured(self, mock_check_flags):
+ self.library.configuration.netapp_lun_ostype = 'windows'
+ self.library.configuration.netapp_host_type = 'solaris'
self.library.do_setup(mock.Mock())
+ self.assertTrue(mock_check_flags.called)
+ self.assertEqual('windows', self.library.lun_ostype)
+ self.assertEqual('solaris', self.library.host_type)
+ @mock.patch.object(na_utils, 'check_flags')
+ def test_do_setup_san_unconfigured(self, mock_check_flags):
+ self.library.configuration.netapp_lun_ostype = None
+ self.library.configuration.netapp_host_type = None
+ self.library.do_setup(mock.Mock())
self.assertTrue(mock_check_flags.called)
+ self.assertEqual('linux', self.library.lun_ostype)
+ self.assertEqual('linux', self.library.host_type)
def test_get_existing_vol_manage_missing_id_path(self):
self.assertRaises(exception.ManageExistingInvalidReference,
target_details_list, filter)
self.assertEqual(target_details_list[1], result)
+
+ @mock.patch.object(na_utils, 'check_flags', mock.Mock())
+ @mock.patch.object(block_base, 'LOG', mock.Mock())
+ def test_setup_error_invalid_lun_os(self):
+ self.library.configuration.netapp_lun_ostype = 'unknown'
+ self.library.do_setup(mock.Mock())
+ self.assertRaises(exception.NetAppDriverException,
+ self.library.check_for_setup_error)
+ msg = _("Invalid value for NetApp configuration"
+ " option netapp_lun_ostype.")
+ block_base.LOG.error.assert_called_once_with(msg)
+
+ @mock.patch.object(na_utils, 'check_flags', mock.Mock())
+ @mock.patch.object(block_base, 'LOG', mock.Mock())
+ def test_setup_error_invalid_host_type(self):
+ self.library.configuration.netapp_lun_ostype = 'linux'
+ self.library.configuration.netapp_host_type = 'future_os'
+ self.library.do_setup(mock.Mock())
+ self.assertRaises(exception.NetAppDriverException,
+ self.library.check_for_setup_error)
+ msg = _("Invalid value for NetApp configuration"
+ " option netapp_host_type.")
+ block_base.LOG.error.assert_called_once_with(msg)
+
+ @mock.patch.object(na_utils, 'check_flags', mock.Mock())
+ def test_check_for_setup_error_both_config(self):
+ self.library.configuration.netapp_lun_ostype = 'linux'
+ self.library.configuration.netapp_host_type = 'linux'
+ self.library.do_setup(mock.Mock())
+ self.zapi_client.get_lun_list.return_value = ['lun1']
+ self.library._extract_and_populate_luns = mock.Mock()
+ self.library.check_for_setup_error()
+ self.library._extract_and_populate_luns.assert_called_once_with(
+ ['lun1'])
+
+ @mock.patch.object(na_utils, 'check_flags', mock.Mock())
+ def test_check_for_setup_error_no_os_host(self):
+ self.library.configuration.netapp_lun_ostype = None
+ self.library.configuration.netapp_host_type = None
+ self.library.do_setup(mock.Mock())
+ self.zapi_client.get_lun_list.return_value = ['lun1']
+ self.library._extract_and_populate_luns = mock.Mock()
+ self.library.check_for_setup_error()
+ self.library._extract_and_populate_luns.assert_called_once_with(
+ ['lun1'])
IGROUP_PREFIX = 'openstack-'
REQUIRED_FLAGS = ['netapp_login', 'netapp_password',
'netapp_server_hostname']
+ ALLOWED_LUN_OS_TYPES = ['linux', 'aix', 'hpux', 'image', 'windows',
+ 'windows_2008', 'windows_gpt', 'solaris',
+ 'solaris_efi', 'netware', 'openvms', 'hyper_v']
+ ALLOWED_IGROUP_HOST_TYPES = ['linux', 'aix', 'hpux', 'windows', 'solaris',
+ 'netware', 'default', 'vmware', 'openvms',
+ 'xen', 'hyper_v']
+ DEFAULT_LUN_OS = 'linux'
+ DEFAULT_HOST_TYPE = 'linux'
def __init__(self, driver_name, driver_protocol, **kwargs):
self.zapi_client = None
self._stats = {}
self.lun_table = {}
+ self.lun_ostype = None
+ self.host_type = None
self.lookup_service = fczm_utils.create_lookup_service()
self.app_version = kwargs.get("app_version", "unknown")
self.db = kwargs.get('db')
self.configuration.append_config_values(na_opts.netapp_transport_opts)
self.configuration.append_config_values(
na_opts.netapp_provisioning_opts)
+ self.configuration.append_config_values(na_opts.netapp_san_opts)
def do_setup(self, context):
na_utils.check_flags(self.REQUIRED_FLAGS, self.configuration)
+ self.lun_ostype = (self.configuration.netapp_lun_ostype
+ or self.DEFAULT_LUN_OS)
+ self.host_type = (self.configuration.netapp_host_type
+ or self.DEFAULT_HOST_TYPE)
def check_for_setup_error(self):
"""Check that the driver is working and can communicate.
Discovers the LUNs on the NetApp server.
"""
-
+ if self.lun_ostype not in self.ALLOWED_LUN_OS_TYPES:
+ msg = _("Invalid value for NetApp configuration"
+ " option netapp_lun_ostype.")
+ LOG.error(msg)
+ raise exception.NetAppDriverException(msg)
+ if self.host_type not in self.ALLOWED_IGROUP_HOST_TYPES:
+ msg = _("Invalid value for NetApp configuration"
+ " option netapp_host_type.")
+ LOG.error(msg)
+ raise exception.NetAppDriverException(msg)
lun_list = self.zapi_client.get_lun_list()
self._extract_and_populate_luns(lun_list)
LOG.debug("Success getting list of LUNs from server.")
size = default_size if not int(volume['size'])\
else int(volume['size']) * units.Gi
- metadata = {'OsType': 'linux',
+ metadata = {'OsType': self.lun_ostype,
'SpaceReserved': 'true',
'Path': '/vol/%s/%s' % (ontap_volume_name, lun_name)}
def _map_lun(self, name, initiator_list, initiator_type, lun_id=None):
"""Maps LUN to the initiator(s) and returns LUN ID assigned."""
metadata = self._get_lun_attr(name, 'metadata')
- os = metadata['OsType']
path = metadata['Path']
- if self._check_allowed_os(os):
- os = os
- else:
- os = 'default'
- igroup_name = self._get_or_create_igroup(initiator_list,
- initiator_type, os)
+ igroup_name, ig_host_os, ig_type = self._get_or_create_igroup(
+ initiator_list, initiator_type, self.host_type)
+ if ig_host_os != self.host_type:
+ LOG.warning(_LW("LUN misalignment may occur for current"
+ " initiator group %(ig_nm)s) with host OS type"
+ " %(ig_os)s. Please configure initiator group"
+ " manually according to the type of the"
+ " host OS."),
+ {'ig_nm': igroup_name, 'ig_os': ig_host_os})
try:
return self.zapi_client.map_lun(path, igroup_name, lun_id=lun_id)
except na_api.NaApiError:
"""Checks whether any LUNs are mapped to the given initiator(s)."""
return self.zapi_client.has_luns_mapped_to_initiators(initiator_list)
- def _get_or_create_igroup(self, initiator_list, initiator_type,
- os='default'):
+ def _get_or_create_igroup(self, initiator_list, initiator_group_type,
+ host_os_type):
"""Checks for an igroup for a set of one or more initiators.
- Creates igroup if not found.
+ Creates igroup if not already present with given host os type,
+ igroup type and adds initiators.
"""
-
igroups = self.zapi_client.get_igroup_by_initiators(initiator_list)
-
igroup_name = None
- for igroup in igroups:
- if igroup['initiator-group-os-type'] == os:
- if igroup['initiator-group-type'] == initiator_type or \
- igroup['initiator-group-type'] == 'mixed':
- if igroup['initiator-group-name'].startswith(
- self.IGROUP_PREFIX):
- igroup_name = igroup['initiator-group-name']
- break
+
+ if igroups:
+ igroup = igroups[0]
+ igroup_name = igroup['initiator-group-name']
+ host_os_type = igroup['initiator-group-os-type']
+ initiator_group_type = igroup['initiator-group-type']
+
if not igroup_name:
- igroup_name = self.IGROUP_PREFIX + six.text_type(uuid.uuid4())
- self.zapi_client.create_igroup(igroup_name, initiator_type, os)
- for initiator in initiator_list:
- self.zapi_client.add_igroup_initiator(igroup_name, initiator)
+ igroup_name = self._create_igroup_add_initiators(
+ initiator_group_type, host_os_type, initiator_list)
+ return igroup_name, host_os_type, initiator_group_type
+
+ def _create_igroup_add_initiators(self, initiator_group_type,
+ host_os_type, initiator_list):
+ """Creates igroup and adds initiators."""
+ igroup_name = self.IGROUP_PREFIX + six.text_type(uuid.uuid4())
+ self.zapi_client.create_igroup(igroup_name, initiator_group_type,
+ host_os_type)
+ for initiator in initiator_list:
+ self.zapi_client.add_igroup_initiator(igroup_name, initiator)
return igroup_name
- def _check_allowed_os(self, os):
- """Checks if the os type supplied is NetApp supported."""
- if os in ['linux', 'aix', 'hpux', 'windows', 'solaris',
- 'netware', 'vmware', 'openvms', 'xen', 'hyper_v']:
- return True
- else:
- return False
-
def _add_lun_to_table(self, lun):
"""Adds LUN to cache table."""
if not isinstance(lun, NetAppLun):
name = volume['name']
lun_id = self._map_lun(name, [initiator_name], 'iscsi', None)
- msg = _("Mapped LUN %(name)s to the initiator %(initiator_name)s")
+ msg = "Mapped LUN %(name)s to the initiator %(initiator_name)s"
msg_fmt = {'name': name, 'initiator_name': initiator_name}
LOG.debug(msg % msg_fmt)
msg = _('Failed to get LUN target list for the LUN %s')
raise exception.VolumeBackendAPIException(data=msg % name)
- msg = _("Successfully fetched target list for LUN %(name)s and "
- "initiator %(initiator_name)s")
+ msg = ("Successfully fetched target list for LUN %(name)s and "
+ "initiator %(initiator_name)s")
msg_fmt = {'name': name, 'initiator_name': initiator_name}
LOG.debug(msg % msg_fmt)
CONF.register_opts(na_opts.netapp_connection_opts)
CONF.register_opts(na_opts.netapp_eseries_opts)
CONF.register_opts(na_opts.netapp_transport_opts)
+CONF.register_opts(na_opts.netapp_san_opts)
class NetAppEseriesISCSIDriver(driver.ISCSIDriver):
SSC_UPDATE_INTERVAL = 60 # seconds
WORLDWIDENAME = 'worldWideName'
+ DEFAULT_HOST_TYPE = 'linux_dm_mp'
+
def __init__(self, *args, **kwargs):
super(NetAppEseriesISCSIDriver, self).__init__(*args, **kwargs)
na_utils.validate_instantiation(**kwargs)
na_opts.netapp_connection_opts)
self.configuration.append_config_values(na_opts.netapp_transport_opts)
self.configuration.append_config_values(na_opts.netapp_eseries_opts)
+ self.configuration.append_config_values(na_opts.netapp_san_opts)
self._backend_name = self.configuration.safe_get(
"volume_backend_name") or "NetApp_ESeries"
self._objects = {'disk_pool_refs': [], 'pools': [],
self._start_periodic_tasks()
def _check_host_type(self):
- self.host_type =\
- self.HOST_TYPES.get(self.configuration.netapp_eseries_host_type,
- None)
+ host_type = (self.configuration.netapp_host_type
+ or self.DEFAULT_HOST_TYPE)
+ self.host_type = self.HOST_TYPES.get(host_type)
if not self.host_type:
raise exception.NetAppDriverException(
_('Configured host type is not supported.'))
'specified storage pools. Only dynamic disk pools are '
'currently supported. Specify the value of this option to'
' be a comma separated list of disk pool names to be used'
- ' for provisioning.')),
- cfg.StrOpt('netapp_eseries_host_type',
- default='linux_dm_mp',
- help=('This option is used to define how the controllers in '
- 'the E-Series storage array will work with the '
- 'particular operating system on the hosts that are '
- 'connected to it.')), ]
+ ' for provisioning.')), ]
netapp_nfs_extra_opts = [
cfg.StrOpt('netapp_copyoffload_tool_path',
default=None,
'offload tool binary. Ensure that the binary has execute '
'permissions set which allow the effective user of the '
'cinder-volume process to execute the file.')), ]
+netapp_san_opts = [
+ cfg.StrOpt('netapp_lun_ostype',
+ default=None,
+ help=('This option defines the type of operating system that'
+ ' will access a LUN exported from Data ONTAP; it is'
+ ' assigned to the LUN at the time it is created.')),
+ cfg.StrOpt('netapp_host_type',
+ deprecated_name='netapp_eseries_host_type',
+ default=None,
+ help=('This option defines the type of operating system for'
+ ' all initiators that can access a LUN. This information'
+ ' is used when mapping LUNs to individual hosts or'
+ ' groups of hosts.'))]
CONF = cfg.CONF
CONF.register_opts(netapp_proxy_opts)
CONF.register_opts(netapp_img_cache_opts)
CONF.register_opts(netapp_eseries_opts)
CONF.register_opts(netapp_nfs_extra_opts)
+CONF.register_opts(netapp_san_opts)