"index" : 5
}, {
"id" : "6",
- "code" : "LNX",
- "name" : "Linux",
+ "code" : "LnxALUA",
+ "name" : "LnxALUA",
"index" : 6
}]"""
elif re.match("^/storage-systems/[0-9a-zA-Z]+/snapshot-groups$", path):
self.assertRaises(exception.NetAppDriverException,
self.driver._get_iscsi_portal_for_vol,
vol_nomatch, portals, False)
+
+ def test_get_host_right_type(self):
+ self.driver._get_host_with_port = mock.Mock(
+ return_value={'hostTypeIndex': 2, 'name': 'test'})
+ self.driver._get_host_type_definition = mock.Mock(
+ return_value={'index': 2, 'name': 'LnxALUA'})
+ host = self.driver._get_or_create_host('port', 'LinuxALUA')
+ self.assertEqual(host, {'hostTypeIndex': 2, 'name': 'test'})
+ self.driver._get_host_with_port.assert_called_once_with('port')
+ self.driver._get_host_type_definition.assert_called_once_with(
+ 'LinuxALUA')
+
+ def test_get_host_update_type(self):
+ self.driver._get_host_with_port = mock.Mock(
+ return_value={'hostTypeIndex': 2, 'hostRef': 'test'})
+ self.driver._get_host_type_definition = mock.Mock(
+ return_value={'index': 3, 'name': 'LnxALUA'})
+ self.driver._client.update_host_type = mock.Mock(
+ return_value={'hostTypeIndex': 3, 'hostRef': 'test'})
+ host = self.driver._get_or_create_host('port', 'LinuxALUA')
+ self.assertEqual(host, {'hostTypeIndex': 3, 'hostRef': 'test'})
+ self.driver._get_host_with_port.assert_called_once_with('port')
+ self.driver._get_host_type_definition.assert_called_once_with(
+ 'LinuxALUA')
+ self.assertEqual(self.driver._client.update_host_type.call_count, 1)
+
+ def test_get_host_update_type_failed(self):
+ self.driver._get_host_with_port = mock.Mock(
+ return_value={'hostTypeIndex': 2, 'hostRef': 'test',
+ 'label': 'test'})
+ self.driver._get_host_type_definition = mock.Mock(
+ return_value={'index': 3, 'name': 'LnxALUA'})
+ self.driver._client.update_host_type = mock.Mock(
+ side_effect=exception.NetAppDriverException)
+ host = self.driver._get_or_create_host('port', 'LinuxALUA')
+ self.assertEqual(host, {'hostTypeIndex': 2, 'hostRef': 'test',
+ 'label': 'test'})
+ self.driver._get_host_with_port.assert_called_once_with('port')
+ self.driver._get_host_type_definition.assert_called_once_with(
+ 'LinuxALUA')
+ self.assertEqual(self.driver._client.update_host_type.call_count, 1)
+
+ def test_get_host_not_found(self):
+ self.driver._get_host_with_port = mock.Mock(
+ side_effect=exception.NotFound)
+ self.driver._create_host = mock.Mock()
+ self.driver._get_or_create_host('port', 'LnxALUA')
+ self.driver._get_host_with_port.assert_called_once_with('port')
+ self.driver._create_host.assert_called_once_with('port', 'LnxALUA')
+
+ def test_setup_error_unsupported_host_type(self):
+ configuration = self._set_config(create_configuration())
+ configuration.netapp_eseries_host_type = 'garbage'
+ driver = common.NetAppDriver(configuration=configuration)
+ self.assertRaises(exception.NetAppDriverException,
+ driver.check_for_setup_error)
port = {'type': port_type, 'port': port_id, 'label': port_label}
return self.create_host(label, host_type, [port], group_id)
+ def update_host_type(self, host_ref, host_type):
+ """Updates host type for a given host."""
+ path = "/storage-systems/{system-id}/hosts/{object-id}"
+ data = {'hostType': host_type}
+ return self._invoke('POST', path, data, **{'object-id': host_ref})
+
def list_host_types(self):
"""Lists host types in storage system."""
path = "/storage-systems/{system-id}/host-types"
'netapp_storage_pools']
SLEEP_SECS = 5
MAX_LUNS_PER_HOST = 255
+ HOST_TYPES = {'aix': 'AIX MPIO',
+ 'avt': 'AVT_4M',
+ 'factoryDefault': 'FactoryDefault',
+ 'hpux': 'HP-UX TPGS',
+ 'linux_atto': 'LnxTPGSALUA',
+ 'linux_dm_mp': 'LnxALUA',
+ 'linux_mpp_rdac': 'Linux',
+ 'linux_pathmanager': 'LnxTPGSALUA_PM',
+ 'macos': 'MacTPGSALUA',
+ 'ontap': 'ONTAP',
+ 'svc': 'SVC',
+ 'solaris_v11': 'SolTPGSALUA',
+ 'solaris_v10': 'Solaris',
+ 'vmware': 'VmwTPGSALUA',
+ 'windows':
+ 'Windows 2000/Server 2003/Server 2008 Non-Clustered',
+ 'windows_atto': 'WinTPGSALUA',
+ 'windows_clustered':
+ 'Windows 2000/Server 2003/Server 2008 Clustered'
+ }
def __init__(self, *args, **kwargs):
super(Driver, self).__init__(*args, **kwargs)
raise exception.InvalidInput(reason=msg)
def check_for_setup_error(self):
+ self.host_type =\
+ self.HOST_TYPES.get(self.configuration.netapp_eseries_host_type,
+ None)
+ if not self.host_type:
+ raise exception.NetAppDriverException(
+ _('Configured host type is not supported.'))
self._check_storage_system()
self._populate_system_objects()
@cinder_utils.synchronized('map_es_volume')
def _map_volume_to_host(self, vol, initiator):
"""Maps the e-series volume to host with initiator."""
- host = self._get_or_create_host(initiator)
+ host = self._get_or_create_host(initiator, self.host_type)
vol_maps = self._get_host_mapping_for_vol_frm_array(vol)
for vol_map in vol_maps:
if vol_map.get('mapRef') == host['hostRef']:
return self._client.create_volume_mapping(vol['volumeRef'],
host['hostRef'], lun)
- def _get_or_create_host(self, port_id, host_type='linux'):
+ def _get_or_create_host(self, port_id, host_type):
"""Fetch or create a host by given port."""
try:
- return self._get_host_with_port(port_id, host_type)
+ host = self._get_host_with_port(port_id)
+ ht_def = self._get_host_type_definition(host_type)
+ if host.get('hostTypeIndex') == ht_def.get('index'):
+ return host
+ else:
+ try:
+ 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})
+ return host
except exception.NotFound as e:
LOG.warn(_("Message - %s."), e.msg)
return self._create_host(port_id, host_type)
- def _get_host_with_port(self, port_id, host_type='linux'):
+ def _get_host_with_port(self, port_id):
"""Gets or creates a host with given port id."""
hosts = self._client.list_hosts()
- ht_def = self._get_host_type_definition(host_type)
for host in hosts:
- if (host.get('hostTypeIndex') == ht_def.get('index')
- and host.get('hostSidePorts')):
+ if host.get('hostSidePorts'):
ports = host.get('hostSidePorts')
for port in ports:
if (port.get('type') == 'iscsi'
and port.get('address') == port_id):
return host
- msg = _("Host with port %(port)s and type %(type)s not found.")
- raise exception.NotFound(msg % {'port': port_id, 'type': host_type})
+ msg = _("Host with port %(port)s not found.")
+ raise exception.NotFound(msg % {'port': port_id})
- def _create_host(self, port_id, host_type='linux'):
+ def _create_host(self, port_id, host_type):
"""Creates host on system with given initiator as port_id."""
LOG.info(_("Creating host with port %s."), port_id)
label = utils.convert_uuid_to_es_fmt(uuid.uuid4())
return self._client.create_host_with_port(label, host_type,
port_id, port_label)
- def _get_host_type_definition(self, host_type='linux'):
+ def _get_host_type_definition(self, host_type):
"""Gets supported host type if available on storage system."""
host_types = self._client.list_host_types()
for ht in host_types:
'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.')), ]
+ ' 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.')), ]
netapp_nfs_extra_opts = [
cfg.StrOpt('netapp_copyoffload_tool_path',
default=None,
# provisioning. (string value)
#netapp_storage_pools=<None>
+# 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.
+# (string value)
+#netapp_eseries_host_type=linux_dm_mp
+
# If the percentage of available space for an NFS share has
# dropped below the value specified by this option, the NFS
# image cache will be cleaned. (integer value)