]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
Add CHAP support for Huawei driver
authorchenzongliang <chenzongliang@huawei.com>
Thu, 4 Jun 2015 09:35:12 +0000 (17:35 +0800)
committerLiu Xinguo <295988511@qq.com>
Tue, 21 Jul 2015 08:07:07 +0000 (16:07 +0800)
CHAP authentication for iSCSI has already been
implemented in multiple drivers. The patch aims to
add this feature to Huawei driver.

Change-Id: Idb267cbf8cc237878eeb7e1f381a3440699b1342
Implements: blueprint huawei-storage-add-chap-support

cinder/tests/unit/test_huawei_drivers.py
cinder/volume/drivers/huawei/huawei_driver.py
cinder/volume/drivers/huawei/rest_client.py

index 6c5e40a1ac79da2d48d6253066512febf2cfd6bc..8991f77b8566e4632dbd83e580aa5c0cc68cd01d 100644 (file)
@@ -737,6 +737,14 @@ MAP_COMMAND_TO_FAKE_RESPONSE['iscsi_initiator?range=[0-256]/GET'] = (
 MAP_COMMAND_TO_FAKE_RESPONSE['iscsi_initiator/'] = (
     FAKE_ISCSI_INITIATOR_RESPONSE)
 
+MAP_COMMAND_TO_FAKE_RESPONSE['iscsi_initiator/POST'] = (
+    FAKE_ISCSI_INITIATOR_RESPONSE)
+
+MAP_COMMAND_TO_FAKE_RESPONSE['iscsi_initiator/PUT'] = (
+    FAKE_ISCSI_INITIATOR_RESPONSE)
+MAP_COMMAND_TO_FAKE_RESPONSE['iscsi_initiator/'
+                             'iqn.1993-08.debian:01:ec2bff7ac3a3/PUT'] = (
+    FAKE_ISCSI_INITIATOR_RESPONSE)
 # mock host info map
 MAP_COMMAND_TO_FAKE_RESPONSE['host?range=[0-65535]/GET'] = (
     FAKE_GET_ALL_HOST_INFO_RESPONSE)
@@ -805,6 +813,7 @@ class Fake18000Client(rest_client.RestClient):
         self.deviceid = None
         self.test_fail = False
         self.checkFlag = False
+        self.remove_chap_flag = False
 
     def _change_file_mode(self, filepath):
         pass
@@ -1043,6 +1052,34 @@ class Huawei18000ISCSIDriverTestCase(test.TestCase):
                                                      self.configuration)
         self.assertEqual('0', host_os)
 
+    def test_find_chap_info(self):
+        self.driver.restclient.login()
+        tmp_dict = {}
+        iscsi_info = {}
+        tmp_dict['Name'] = 'iqn.1993-08.debian:01:ec2bff7ac3a3'
+        tmp_dict['CHAPinfo'] = 'mm-user;mm-user@storage'
+        ini_list = [tmp_dict]
+        iscsi_info['Initiator'] = ini_list
+        initiator_name = FakeConnector['initiator']
+        chapinfo = self.driver.restclient.find_chap_info(iscsi_info,
+                                                         initiator_name)
+        chap_username, chap_password = chapinfo.split(';')
+        self.assertEqual('mm-user', chap_username)
+        self.assertEqual('mm-user@storage', chap_password)
+
+    def test_find_alua_info(self):
+        self.driver.restclient.login()
+        tmp_dict = {}
+        iscsi_info = {}
+        tmp_dict['Name'] = 'iqn.1993-08.debian:01:ec2bff7ac3a3'
+        tmp_dict['ALUA'] = '1'
+        ini_list = [tmp_dict]
+        iscsi_info['Initiator'] = ini_list
+        initiator_name = FakeConnector['initiator']
+        type = self.driver.restclient._find_alua_info(iscsi_info,
+                                                      initiator_name)
+        self.assertEqual('1', type)
+
     def create_fake_conf_file(self):
         """Create a fake Config file.
 
@@ -1115,6 +1152,8 @@ class Huawei18000ISCSIDriverTestCase(test.TestCase):
         initiator = doc.createElement('Initiator')
         initiator.setAttribute('Name', 'iqn.1993-08.debian:01:ec2bff7ac3a3')
         initiator.setAttribute('TargetIP', '192.168.100.2')
+        initiator.setAttribute('CHAPinfo', 'mm-user;mm-user@storage')
+        initiator.setAttribute('ALUA', '1')
         iscsi.appendChild(initiator)
 
         host = doc.createElement('Host')
index 879892729a2821cc89f977080de3f10d5b6d6eac..8b4bbab493f65acbe7e5a31d3dc49a31d42030c0 100644 (file)
@@ -354,7 +354,9 @@ class HuaweiBaseDriver(driver.VolumeDriver):
                                                       host_name_before_hash)
 
         # Add initiator to the host.
-        self.restclient.ensure_initiator_added(initiator_name, host_id)
+        self.restclient.ensure_initiator_added(self.xml_file_path,
+                                               initiator_name,
+                                               host_id)
         hostgroup_id = self.restclient.add_host_into_hostgroup(host_id)
 
         # Mapping lungroup and hostgroup to view.
@@ -368,6 +370,9 @@ class HuaweiBaseDriver(driver.VolumeDriver):
         LOG.info(_LI("initialize_connection_iscsi, host lun id is: %s."),
                  hostlun_id)
 
+        iscsi_conf = huawei_utils.get_iscsi_conf(self.xml_file_path)
+        chapinfo = self.restclient.find_chap_info(iscsi_conf,
+                                                  initiator_name)
         # Return iSCSI properties.
         properties = {}
         properties['target_discovered'] = False
@@ -376,6 +381,13 @@ class HuaweiBaseDriver(driver.VolumeDriver):
         properties['target_lun'] = int(hostlun_id)
         properties['volume_id'] = volume['id']
 
+        # If use CHAP, return CHAP info.
+        if chapinfo:
+            chap_username, chap_password = chapinfo.split(';')
+            properties['auth_method'] = 'CHAP'
+            properties['auth_username'] = chap_username
+            properties['auth_password'] = chap_password
+
         LOG.info(_LI("initialize_connection_iscsi success. Return data: %s."),
                  properties)
         return {'driver_volume_type': 'iscsi', 'data': properties}
@@ -434,6 +446,8 @@ class HuaweiBaseDriver(driver.VolumeDriver):
                 self.restclient.delete_portgroup_mapping_view(view_id,
                                                               portgroup_id)
         if view_id and (int(left_lunnum) <= 0):
+            self.restclient.remove_chap(initiator_name)
+
             if self.restclient.lungroup_associated(view_id, lungroup_id):
                 self.restclient.delete_lungroup_mapping_view(view_id,
                                                              lungroup_id)
index 0501a35c60a85aaed05bc4163a1333664b780ea7..5e1af6214475c36b4200ecca1856ed7105afd4f1 100644 (file)
@@ -388,12 +388,14 @@ class RestClient(object):
 
         return lun_id
 
-    def ensure_initiator_added(self, initiator_name, host_id):
+    def ensure_initiator_added(self, xml_file_path, initiator_name, host_id):
         added = self._initiator_is_added_to_array(initiator_name)
         if not added:
             self._add_initiator_to_array(initiator_name)
         if not self.is_initiator_associated_to_host(initiator_name):
-            self._associate_initiator_to_host(initiator_name, host_id)
+            self._associate_initiator_to_host(xml_file_path,
+                                              initiator_name,
+                                              host_id)
 
     def _get_iscsi_tgt_port(self):
         url = self.url + "/iscsidevicename"
@@ -681,17 +683,107 @@ class RestClient(object):
         result = self.call(url, data)
         self._assert_rest_result(result, 'Add initiator to array error.')
 
-    def _associate_initiator_to_host(self, ininame, host_id):
-        """Associate initiator with the host."""
-        url = self.url + "/iscsi_initiator/" + ininame
+    def _add_initiator_to_host(self, initiator_name, host_id):
+        url = self.url + "/iscsi_initiator/" + initiator_name
         data = json.dumps({"TYPE": "222",
-                           "ID": ininame,
+                           "ID": initiator_name,
                            "USECHAP": "false",
                            "PARENTTYPE": "21",
                            "PARENTID": host_id})
         result = self.call(url, data, "PUT")
         self._assert_rest_result(result, 'Associate initiator to host error.')
 
+    def _associate_initiator_to_host(self,
+                                     xml_file_path,
+                                     initiator_name,
+                                     host_id):
+        """Associate initiator with the host."""
+        iscsi_conf = huawei_utils.get_iscsi_conf(xml_file_path)
+        chapinfo = None
+        multipathtype = None
+
+        chapinfo = self.find_chap_info(iscsi_conf,
+                                       initiator_name)
+        multipathtype = self._find_alua_info(iscsi_conf,
+                                             initiator_name)
+        if chapinfo:
+            LOG.info(_LI('Use CHAP when adding initiator to host.'))
+            self._use_chap(chapinfo, initiator_name, host_id)
+        else:
+            self._add_initiator_to_host(initiator_name, host_id)
+
+        if multipathtype:
+            LOG.info(_LI('Use ALUA when adding initiator to host.'))
+            self._use_alua(initiator_name, multipathtype)
+
+    def find_chap_info(self, iscsi_conf, initiator_name):
+        """Find CHAP info from xml."""
+        chapinfo = None
+        for ini in iscsi_conf['Initiator']:
+            if ini['Name'] == initiator_name:
+                if 'CHAPinfo' in ini:
+                    chapinfo = ini['CHAPinfo']
+                    break
+
+        return chapinfo
+
+    def _find_alua_info(self, iscsi_conf, initiator_name):
+        """Find ALUA info from xml."""
+        multipathtype = 0
+        for ini in iscsi_conf['Initiator']:
+            if ini['Name'] == initiator_name:
+                if 'ALUA' in ini:
+                    if ini['ALUA'] != '1' and ini['ALUA'] != '0':
+                        msg = (_(
+                            'Invalid ALUA value. '
+                            'ALUA value must be 1 or 0.'))
+                        LOG.error(msg)
+                        raise exception.InvalidInput(msg)
+                    else:
+                        multipathtype = ini['ALUA']
+                        break
+        return multipathtype
+
+    def _use_chap(self, chapinfo, initiator_name, host_id):
+        """Use CHAP when adding initiator to host."""
+        (chap_username, chap_password) = chapinfo.split(";")
+
+        url = self.url + "/iscsi_initiator/" + initiator_name
+        data = json.dumps({"TYPE": "222",
+                           "USECHAP": "true",
+                           "CHAPNAME": chap_username,
+                           "CHAPPASSWORD": chap_password,
+                           "ID": initiator_name,
+                           "PARENTTYPE": "21",
+                           "PARENTID": host_id})
+        result = self.call(url, data, "PUT")
+
+        self._assert_rest_result(result,
+                                 'Use CHAP to associate initiator '
+                                 'to host error. Please check the CHAP '
+                                 'username and password.')
+
+    def _use_alua(self, initiator_name, multipathtype):
+        """Use ALUA when adding initiator to host."""
+        url = self.url + "/iscsi_initiator"
+        data = json.dumps({"ID": initiator_name,
+                           "MULTIPATHTYPE": multipathtype})
+        result = self.call(url, data, "PUT")
+
+        self._assert_rest_result(result,
+                                 'Use ALUA to associate initiator '
+                                 'to host error.')
+
+    def remove_chap(self, initiator_name):
+        """Remove CHAP when terminate connection."""
+        url = self.url + "/iscsi_initiator"
+        data = json.dumps({"USECHAP": "false",
+                           "MULTIPATHTYPE": "0",
+                           "ID": initiator_name})
+        result = self.call(url, data, "PUT")
+
+        self._assert_rest_result(result, 'Remove CHAP error.')
+
     def find_mapping_view(self, name):
         """Find mapping view."""
         url = self.url + "/mappingview?range=[0-8191]"