]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
NetApp fix for default host type in eseries
authorNavneet Singh <singn@netapp.com>
Fri, 23 May 2014 04:57:33 +0000 (10:27 +0530)
committerThomas Goirand <thomas@goirand.fr>
Sun, 14 Dec 2014 09:18:30 +0000 (09:18 +0000)
This fixes the issue where the default host type
provided in mapping should be high performing LnxALUA
type for eseries. It also makes it configurable in case
users want to configure a different host type.

Closes-Bug: #1365884

Change-Id: I30992ca69c25c3c02334470aae90c32731a5f3f4
(cherry picked from commit a120ede9ae3f18756db07d1d6696b9ac773b84bf)

cinder/tests/test_netapp_eseries_iscsi.py
cinder/volume/drivers/netapp/eseries/client.py
cinder/volume/drivers/netapp/eseries/iscsi.py
cinder/volume/drivers/netapp/options.py
etc/cinder/cinder.conf.sample

index 5d2b983494bf7d6b6e7ba131eb1c75fc095607fa..e9b6a2ced3a5e38f31dc6c0a54c1dc343138298c 100644 (file)
@@ -216,8 +216,8 @@ class FakeEseriesServerHandler(object):
                       "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):
@@ -878,3 +878,59 @@ class NetAppEseriesIscsiDriverTestCase(test.TestCase):
         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)
index 52b376a2f5a8d8b3928b9b2d3cc6c46b29ea5615..3b144f91f1cf9b98ebfb91fe7e3b9a5c15d32609 100644 (file)
@@ -217,6 +217,12 @@ class RestClient(WebserviceClient):
         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"
index bfb08d79ff37edca9c4b48d688773d9bff1c3493..4f76baface39602457c31297ea31430d36c6d562 100644 (file)
@@ -58,6 +58,26 @@ class Driver(driver.ISCSIDriver):
                       '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)
@@ -91,6 +111,12 @@ class Driver(driver.ISCSIDriver):
                 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()
 
@@ -579,7 +605,7 @@ class Driver(driver.ISCSIDriver):
     @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']:
@@ -592,30 +618,40 @@ class Driver(driver.ISCSIDriver):
         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())
@@ -624,7 +660,7 @@ class Driver(driver.ISCSIDriver):
         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:
index 8995d438b5350f10173843c0f6559c77cb17ee4f..5b01d1f5864849b3baa9a1cabd05a4a48b46b25a 100644 (file)
@@ -161,7 +161,13 @@ netapp_eseries_opts = [
                      '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,
index de342bd3712f383a0f7b4f9e3bc3ce639ecc13c5..b66275be0bc954944ef0fa4630a8fdc57696eb46 100644 (file)
 # 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)